-
Notifications
You must be signed in to change notification settings - Fork 6
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
795 Care about where a plan is sourced #807
Changes from 5 commits
8fc384a
2c61550
1d617e4
abcab4c
5f44781
30acc5b
21d7352
62f60b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -16,7 +16,11 @@ | |||||||||||||||||
|
||||||||||||||||||
from blueapi import utils | ||||||||||||||||||
from blueapi.config import EnvironmentConfig, SourceKind | ||||||||||||||||||
from blueapi.utils import BlueapiPlanModelConfig, load_module_all | ||||||||||||||||||
from blueapi.utils import ( | ||||||||||||||||||
BlueapiPlanModelConfig, | ||||||||||||||||||
is_function_sourced_from_module, | ||||||||||||||||||
load_module_all, | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
from .bluesky_types import ( | ||||||||||||||||||
BLUESKY_PROTOCOLS, | ||||||||||||||||||
|
@@ -99,7 +103,15 @@ def plan_2(...) -> MsgGenerator: | |||||||||||||||||
""" | ||||||||||||||||||
|
||||||||||||||||||
for obj in load_module_all(module): | ||||||||||||||||||
if is_bluesky_plan_generator(obj): | ||||||||||||||||||
# The rule here is that we only inspect objects defined in the module | ||||||||||||||||||
# (as opposed to objects imported from other modules) to determine if | ||||||||||||||||||
# they are valid plans, unless there is an __all__ defined in the module, | ||||||||||||||||||
# in which case we only inspect objects listed there, regardless of their | ||||||||||||||||||
# original source module. | ||||||||||||||||||
if ( | ||||||||||||||||||
hasattr(module, "__all__") | ||||||||||||||||||
or is_function_sourced_from_module(obj, module) | ||||||||||||||||||
) and is_bluesky_plan_generator(obj): | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Currently we import the module for every object that is found in the module- reversing this check will a. cut down on the number of times we try and import the module, and b. allow |
||||||||||||||||||
self.register_plan(obj) | ||||||||||||||||||
|
||||||||||||||||||
def with_device_module(self, module: ModuleType) -> None: | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import importlib | ||
from collections.abc import Iterable | ||
from types import ModuleType | ||
from typing import Any | ||
|
@@ -34,3 +35,17 @@ def get_named_subset(names: list[str]): | |
for name, value in mod.__dict__.items(): | ||
if not name.startswith("_"): | ||
yield value | ||
|
||
|
||
def is_function_sourced_from_module(obj: Any, module: ModuleType) -> bool: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If not doing the above change of order of checking if obj is instance of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going with your first suggestion, I'm not aware of any constraint that guarantees that something being There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just |
||
""" | ||
Check if an object is originally from a particular module, useful to detect | ||
whether it actually comes from a nested import. | ||
|
||
Args: | ||
obj: Object to check | ||
module: Module to check against object | ||
""" | ||
return ( | ||
hasattr(obj, "__module__") and importlib.import_module(obj.__module__) is module | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When this is being used over all of the objects in a module, is it going to import the module and run any side effects for every object? This could (in plan modules out of our control) be dangerous. Or does it cache and only import the module once? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It caches # tt.py
print("Hello world") >>> import importlib
>>> importlib.import_module("tt")
Hello world
<module 'tt' from '/workspaces/blueapi/tt.py'>
>>> importlib.import_module("tt")
<module 'tt' from '/workspaces/blueapi/tt.py'> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from bluesky.utils import MsgGenerator | ||
from tests.unit_tests.core.fake_plan_module import plan_a, plan_b # noqa: F401 | ||
|
||
|
||
def plan_c(c: bool) -> MsgGenerator[None]: ... | ||
def plan_d(d: int) -> MsgGenerator[int]: ... | ||
|
||
|
||
__all__ = ["plan_a", "plan_d"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from bluesky.utils import MsgGenerator | ||
from tests.unit_tests.core.fake_plan_module import plan_a, plan_b # noqa: F401 | ||
|
||
|
||
def plan_c(c: bool) -> MsgGenerator[None]: ... | ||
def plan_d(d: int) -> MsgGenerator[int]: ... |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -170,6 +170,20 @@ def test_add_plan_from_module(empty_context: BlueskyContext) -> None: | |||||
assert EXPECTED_PLANS == empty_context.plans.keys() | ||||||
|
||||||
|
||||||
def test_only_plans_from_source_module_detectede(empty_context: BlueskyContext) -> None: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit |
||||||
import tests.unit_tests.core.fake_plan_module_with_imports as plan_module | ||||||
|
||||||
empty_context.with_plan_module(plan_module) | ||||||
assert {"plan_c", "plan_d"} == empty_context.plans.keys() | ||||||
|
||||||
|
||||||
def test_only_plans_from_all_in_module_detectede(empty_context: BlueskyContext) -> None: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit |
||||||
import tests.unit_tests.core.fake_plan_module_with_all as plan_module | ||||||
|
||||||
empty_context.with_plan_module(plan_module) | ||||||
assert {"plan_a", "plan_d"} == empty_context.plans.keys() | ||||||
|
||||||
|
||||||
def test_add_named_device(empty_context: BlueskyContext, sim_motor: SynAxis) -> None: | ||||||
empty_context.register_device(sim_motor) | ||||||
assert empty_context.devices[SIM_MOTOR_NAME] is sim_motor | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
def a(): ... | ||
|
||
|
||
def b(): ... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from .functions_a import a, b # noqa: F401 | ||
|
||
|
||
def c(): ... | ||
|
||
|
||
def d(): ... | ||
|
||
|
||
e = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must simpyl -> simply or just being removed: I've decided to be opposed to adverbs in documentation- it doesn't matter if it is simply done or complexly done, only that it is done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, master yoda