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

Allow aliased or extended builtin imports in device modules #61

Merged
merged 3 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions src/dodal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,11 @@ def _is_device_skipped(func: Callable[..., Any]) -> bool:


def _is_device_factory(func: Callable[..., Any]) -> bool:
return_type = signature(func).return_annotation
return _is_device_type(return_type)
try:
return_type = signature(func).return_annotation
return _is_device_type(return_type)
except ValueError:
return False


def _is_device_type(obj: Type[Any]) -> bool:
Expand Down
47 changes: 47 additions & 0 deletions tests/fake_beamline_misbehaving_builtins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from math import hypot, log
from typing import TypedDict

from ophyd.utils import DisconnectedError

"""
Some builtins (e.g. dict, Exception), types that extend
them, and aliases for them, do not have signature information.
PEP-8 recommends being conservative with adding typing information
to builtins and the core Python library, so this may change slowly.
This beamline uses some types or constructions that are known to
cause issue but that could conceivably be used in a beamline file.

- Importing specific exceptions
- Importing functions from builtins, including math
- Aliasing builtins, including dict
- Defining a class that extends TypedDict (e.g. for parameters)

"""


def not_a_device() -> None:
"""
Importing DisconnectedError is enough to cause issue, but we
use it here to prevent linting from removing it from the imports.
"""
raise DisconnectedError()


def also_not_a_device() -> float:
"""
log and hypot both do not have signatures.
Not required to actually be used, importing is enough.
"""
return log(hypot(0, 0))


a = dict
b = Exception


class B(TypedDict):
"""
Causes issue only if a class that extends TypedDict exists.
"""

foo: int
7 changes: 7 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ def test_get_hostname() -> None:
assert get_hostname() == "a"


def test_no_signature_builtins_not_devices() -> None:
import tests.fake_beamline_misbehaving_builtins as fake_beamline

devices = make_all_devices(fake_beamline)
assert not devices


def device_a() -> Readable:
return MagicMock()

Expand Down