From 8e3c7bbfe80d31d8114650cc27cae2bf708cc5d1 Mon Sep 17 00:00:00 2001 From: qwe67581 Date: Wed, 17 May 2023 15:42:31 +0100 Subject: [PATCH] Allow aliased or extended builtin imports in device modules - Closes #56 --- src/dodal/utils.py | 7 ++- tests/fake_beamline_misbehaving_builtins.py | 47 +++++++++++++++++++++ tests/test_utils.py | 6 +++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/fake_beamline_misbehaving_builtins.py diff --git a/src/dodal/utils.py b/src/dodal/utils.py index bc827a53d9..5293b6e96d 100644 --- a/src/dodal/utils.py +++ b/src/dodal/utils.py @@ -166,8 +166,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: diff --git a/tests/fake_beamline_misbehaving_builtins.py b/tests/fake_beamline_misbehaving_builtins.py new file mode 100644 index 0000000000..1b5eed38d1 --- /dev/null +++ b/tests/fake_beamline_misbehaving_builtins.py @@ -0,0 +1,47 @@ +from typing import TypedDict + +from ophyd.utils import DisconnectedError +from math import hypot, log + + +""" + 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 diff --git a/tests/test_utils.py b/tests/test_utils.py index 23361431ef..aa506f5a0c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -52,6 +52,12 @@ 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()