Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(api, performance-metrics): clean up performance-metrics tracking #15289

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
mypy is fighting back
  • Loading branch information
DerekMaggio committed May 30, 2024
commit 40da8f60565ed19ae7a6923c20295373e0c52643
82 changes: 63 additions & 19 deletions api/src/opentrons/util/performance_helpers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"""Performance helpers for tracking robot context."""

import asyncio
import functools
import inspect
from pathlib import Path
from opentrons_shared_data.performance.dev_types import (
SupportsTracking,
F,
UnderlyingFunction,
UnderlyingFunctionParameters,
UnderlyingFunctionReturn,
RobotContextState,
)
from opentrons_shared_data.robot.dev_types import RobotTypeEnum
from typing import Callable, Type
import typing
from opentrons.config import (
get_performance_metrics_data_dir,
robot_configs,
Expand All @@ -21,30 +22,34 @@
RobotTypeEnum.robot_literal_to_enum(robot_configs.load().model)
)


class StubbedTracker(SupportsTracking):
"""A stubbed tracker that does nothing."""

def __init__(
self, storage_location: Path, should_track: bool
) -> None:
def __init__(self, storage_location: Path, should_track: bool) -> None:
"""Initialize the stubbed tracker."""
pass

def track(self, state: RobotContextState) -> Callable[[F], F]:
async def track(
self,
func_to_track: UnderlyingFunction,
state: RobotContextState,
*args: UnderlyingFunctionParameters.args,
**kwargs: UnderlyingFunctionParameters.kwargs
) -> UnderlyingFunctionReturn:
"""Return the function unchanged."""

def inner_decorator(func: F) -> F:
"""Return the function unchanged."""
return func

return inner_decorator
if inspect.iscoroutinefunction(func_to_track):
return await func_to_track(*args, **kwargs) # type: ignore
else:
return func_to_track(*args, **kwargs) # type: ignore

def store(self) -> None:
"""Do nothing."""
pass


def _handle_package_import() -> Type[SupportsTracking]:
def _handle_package_import() -> typing.Type[SupportsTracking]:
"""Handle the import of the performance_metrics package.

If the package is not available, return a stubbed tracker.
Expand All @@ -71,15 +76,54 @@ def _get_robot_context_tracker() -> SupportsTracking:
return _robot_context_tracker


def track_analysis(func: F) -> F:
# def _build_track_wrapper(
# func: UnderlyingFunction, state: RobotContextState
# ) -> WrappedFunction:
# """Decorator to track the given state for the decorated function.

# Args:
# func: The function to track.
# state: The state of the robot context during the function execution.

# Returns:
# The decorated function.
# """

# async def wrapper(
# *args: P.args, **kwargs: P.kwargs
# ) -> T:
# tracker: SupportsTracking = _get_robot_context_tracker()

# try:

# result: T = await tracker.track(
# func_to_track=func, state=state, *args, **kwargs
# )
# finally:
# tracker.store()

# return result

# return wrapper


def track_analysis(func: UnderlyingFunction) -> UnderlyingFunction:
"""Track the analysis of a protocol and optionally store each run."""
@functools.wraps(func)
async def wrapper(*args, **kwargs): # type: ignore # noqa: ANN002, ANN003, ANN201

async def wrapper(
*args: UnderlyingFunctionParameters.args,
**kwargs: UnderlyingFunctionParameters.kwargs
) -> UnderlyingFunctionReturn:
tracker: SupportsTracking = _get_robot_context_tracker()

try:
return await tracker.track(func, RobotContextState.ANALYZING_PROTOCOL, *args, **kwargs)

result: UnderlyingFunctionReturn = await tracker.track(
func_to_track=func, state=RobotContextState.ANALYZING_PROTOCOL
)
finally:
tracker.store()

return wrapper # type: ignore
return result

return wrapper
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
SupportsTracking,
MetricsMetadata,
UnderlyingFunction,
UnderlyingFunctionParams,
UnderlyingFunctionParameters,
UnderlyingFunctionReturn,
)

Expand Down Expand Up @@ -65,8 +65,8 @@ def __init__(self, storage_location: Path, should_track: bool) -> None:
async def __call_function(
self,
func_to_track: UnderlyingFunction,
*args: UnderlyingFunctionParams.args,
**kwargs: UnderlyingFunctionParams.kwargs
*args: UnderlyingFunctionParameters.args,
**kwargs: UnderlyingFunctionParameters.kwargs
) -> UnderlyingFunctionReturn:
"""Calls the given function and returns its result."""
if inspect.iscoroutinefunction(func_to_track):
Expand All @@ -78,8 +78,8 @@ async def track(
self,
func_to_track: UnderlyingFunction,
state: RobotContextState,
*args: UnderlyingFunctionParams.args,
**kwargs: UnderlyingFunctionParams.kwargs
*args: UnderlyingFunctionParameters.args,
**kwargs: UnderlyingFunctionParameters.kwargs
) -> UnderlyingFunctionReturn:
"""Tracks the given function and its execution duration.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
from enum import Enum


UnderlyingFunction = typing.Callable[..., typing.Awaitable[typing.Any] | typing.Any]
UnderlyingFunctionParameters = typing.ParamSpec("UnderlyingFunctionParameters")
UnderlyingFunctionReturn = typing.TypeVar("UnderlyingFunctionReturn")
UnderlyingFunctionParams = typing.ParamSpec("UnderlyingFunctionParams")
UnderlyingFunction = typing.TypeVar(
"UnderlyingFunction", bound=typing.Callable[..., typing.Any]
)


class SupportsTracking(typing.Protocol):
Expand All @@ -23,8 +21,8 @@ async def track(
self,
func_to_track: UnderlyingFunction,
state: "RobotContextState",
*args: UnderlyingFunctionParams.args,
**kwargs: UnderlyingFunctionParams.kwargs,
*args: UnderlyingFunctionParameters.args,
**kwargs: UnderlyingFunctionParameters.kwargs,
) -> UnderlyingFunctionReturn:
"""Decorator to track the given state for the decorated function."""
...
Expand Down