Pyepics migration - #87
Anthony Sligar (sligara7) wants to merge 7 commits into
Conversation
…s to startup, update tests
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Critical lint, compatibility, and test-collection issues remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Migrates photon-delivery devices to a modular ophyd_async package while updating tests, profile configuration, hardware settings, and dependencies.
Changes:
- Splits shutter, slit, filter, and DCLM implementations into separate modules.
- Updates motor, detector, profile, and filter configuration.
- Reorganizes tests and updates project dependencies.
File summaries
| File | Reviewed change |
|---|---|
tests/test_photon_delivery_system.py |
Replaced monolithic PDS tests. |
tests/photon_delivery_system/test_slits.py |
Adds slit behavior tests. |
tests/photon_delivery_system/test_shutter.py |
Adds shutter tests. |
tests/photon_delivery_system/test_filters.py |
Adds filter tests. |
tests/photon_delivery_system/test_dclm.py |
Adds DCLM and tuning tests. |
src/hextools/utils.py |
Adds device lookup and tree utilities. |
src/hextools/profiles/collection.py |
Updates beamline device setup. |
src/hextools/photon_delivery_system/slits.py |
Implements asynchronous slits. |
src/hextools/photon_delivery_system/shutter.py |
Implements shutter control. |
src/hextools/photon_delivery_system/filters.yml |
Updates filter motor PVs. |
src/hextools/photon_delivery_system/filters.py |
Implements filter devices and configuration. |
src/hextools/photon_delivery_system/dclm.py |
Implements DCLM and energy plans. |
src/hextools/photon_delivery_system/__init__.py |
Exposes the modular PDS API. |
src/hextools/photon_delivery_system.py |
Removes the former monolithic module. |
src/hextools/motors.py |
Updates camera objective handling. |
src/hextools/detectors/kinetix.py |
Adds a Kinetix factory. |
pyproject.toml |
Updates runtime dependencies. |
pixi.toml |
Updates environment dependencies. |
Review details
Suppressed comments (8)
src/hextools/motors.py:176
- These new class identifiers use underscores and lowercase segments, which violates the repository's enabled Ruff N801 CapWords rule (
pyproject.toml:147) and will fail the pre-commit lint. Use CapWords names and update callers, or add a narrowly scoped exception if the hardware spelling must be retained.
class FOV_2_4_mm_Camera(StandardReadable, EpicsDevice, AsyncMovable[CameraObjective | str]):
src/hextools/motors.py:228
- A caller passing the string value of either enum member (for example,
CameraObjective.LEFT_4MM.value, which is"Left Objective") is rejected because matching only checks the enum nameLEFT_4MM. Since this setter explicitly accepts strings, also match the enum values or remove the string form from the API.
if isinstance(value, str):
for possible_value in CameraObjective:
if value.upper() in possible_value.name:
value = possible_value
break
src/hextools/motors.py:234
- The new error text concatenates
matchingandone, so invalid inputs reportmatchingone of its names.This makes the validation failure harder to understand; include the missing space in the adjacent literal.
f"Invalid objective value: {value}. "
"Must be a CameraObjective or a string matching" \
"one of its names."
src/hextools/motors.py:226
- Substring matching accepts an empty string because
"" in possible_value.nameis true, so an invalid string silently selectsRIGHT_2MMinstead of raisingValueError. Normalize and compare exact enum names/values before accepting string input.
for possible_value in CameraObjective:
if value.upper() in possible_value.name:
src/hextools/photon_delivery_system/slits.py:32
- The constructor no longer accepts the old
(prefix, num, name)form, but both new slit tests still callSlits("XF:TEST:", 1), so1is bound tonameand the{Slt:1-Ax:portion is absent from the motor PVs. Either retain/normalize the old API or update those fixtures to pass the complete prefix explicitly.
def __init__(self, prefix: str, name: str = ""):
src/hextools/profiles/collection.py:198
- Commenting out the beam-current suspender removes the profile's existing protection against continuing RunEngine work below 100 mA. Unless an equivalent suspender is installed elsewhere, scans will no longer pause on beam loss; this should not be disabled as part of the migration.
# RE.install_suspender(SuspendFloor(storage_ring.beam_current, 100, resume_thresh=390))
src/hextools/profiles/collection.py:211
- This comments out the only
SupplementalDataregistration, so every run loses the established baseline metadata for beam current, sample tower, DCLM, and optics table. Re-enable the registration with the current device names rather than silently dropping this run metadata.
# sd = bpp.SupplementalData(
# baseline=[
# storage_ring.beam_current,
# wb_slits,
# pb_slits,
# sample_tower,
# #dclm,
# optics_table,
# ]
# )
# RE.preprocessors.append(sd)
tests/photon_delivery_system/test_slits.py:77
- This repeats the same outdated two-argument construction as the fixture:
1is treated asname, so this test does not cover the intended slit-1 PV prefix and will fail type checking. Construct the device with the full{Slt:1-Ax:prefix instead.
slits = Slits("XF:TEST:", 1)
- Files reviewed: 18/19 changed files
- Comments generated: 9
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| from ophyd_async.epics.adkinetix import KinetixDetector | ||
| from ophyd_async.epics.adcore import ADWriterFactory | ||
|
|
||
| def kinetix_factory(num: int, path_provider, name: str): |
|
|
||
|
|
||
| class DoubleObjCamera(StandardReadable, EpicsDevice, AsyncMovable[CameraObjective]): | ||
| class FOV_2_4_mm_Camera(StandardReadable, EpicsDevice, AsyncMovable[CameraObjective | str]): |
|
|
||
|
|
||
| class WideFOVCamera(StandardReadable, EpicsDevice): | ||
| class FOV_20_40_mm_Camera(StandardReadable, EpicsDevice): |
| from ophyd_async.epics.adcore import ADWriterFactory, NDStatsIO, PluginSignalDataLogic, ContAcqDetector | ||
| from ophyd_async.epics.adkinetix import KinetixDetector | ||
| from ophyd_async.epics.advimba import VimbaDetector |
| from .dclm import DCLM, change_energy | ||
| from .filters import Filter, FilterPosition, load_filters | ||
| from .shutter import Shutter | ||
| from .slits import Slits | ||
|
|
||
| __all__ = [ | ||
| "Shutter", | ||
| "Filter", | ||
| "load_filters", | ||
| "FilterPosition", | ||
| "Slits", | ||
| "DCLM", | ||
| "change_energy", | ||
| ] |
| def change_energy( | ||
| energy: float, | ||
| dclm: DCLM | None = None, |
| if len(value) == 2 and all(len(v) == 2 for v in value): | ||
| (h_gap, h_center), (v_gap, v_center) = value |
| The indentation level for the current device. | ||
| """ | ||
| x = [] | ||
| _make_tree_body(x, device) |
| @pytest.fixture | ||
| async def slits() -> Slits: | ||
| async with init_devices(mock=True): | ||
| device = Slits("XF:TEST:", 1) |
…ing the branch's lock Both locks are format v7. `pixi lock` against the merged pixi.toml/pyproject.toml reports the branch's lock already up to date, so it is kept as-is; main's lock changes (PR 84 v7 upgrade, PR 85) are already covered by the branch's later bump.
No description provided.