Skip to content

Pyepics migration - #87

Open
Anthony Sligar (sligara7) wants to merge 7 commits into
NSLS2:mainfrom
sligara7:pyepics-migration
Open

Anthony Sligar (sligara7) wants to merge 7 commits into
NSLS2:mainfrom
sligara7:pyepics-migration

Conversation

@sligara7

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI lite review requested due to automatic review settings September 10, 2026 14:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 name LEFT_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 matching and one, so invalid inputs report matchingone 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.name is true, so an invalid string silently selects RIGHT_2MM instead of raising ValueError. 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 call Slits("XF:TEST:", 1), so 1 is bound to name and 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 SupplementalData registration, 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: 1 is treated as name, 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.

Comment on lines +3 to +6
from ophyd_async.epics.adkinetix import KinetixDetector
from ophyd_async.epics.adcore import ADWriterFactory

def kinetix_factory(num: int, path_provider, name: str):
Comment thread src/hextools/motors.py


class DoubleObjCamera(StandardReadable, EpicsDevice, AsyncMovable[CameraObjective]):
class FOV_2_4_mm_Camera(StandardReadable, EpicsDevice, AsyncMovable[CameraObjective | str]):
Comment thread src/hextools/motors.py


class WideFOVCamera(StandardReadable, EpicsDevice):
class FOV_20_40_mm_Camera(StandardReadable, EpicsDevice):
Comment on lines +23 to 25
from ophyd_async.epics.adcore import ADWriterFactory, NDStatsIO, PluginSignalDataLogic, ContAcqDetector
from ophyd_async.epics.adkinetix import KinetixDetector
from ophyd_async.epics.advimba import VimbaDetector
Comment on lines +3 to +16
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",
]
Comment on lines +146 to +148
def change_energy(
energy: float,
dclm: DCLM | None = None,
Comment on lines +172 to +173
if len(value) == 2 and all(len(v) == 2 for v in value):
(h_gap, h_center), (v_gap, v_center) = value
Comment thread src/hextools/utils.py
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants