methods: typed commands — positional arguments and a return value - #419
methods: typed commands — positional arguments and a return value#419coretl wants to merge 3 commits into
Conversation
Lifts the void/void restriction on `Command`. A `@command` may now take positional arguments and return a value, both of known types, and the captured `inspect.Signature` becomes the public description transports read. - `Command[P, T]` is generic over its parameters and return type; `__call__` and `bind` forward arguments. `argument_types`, `return_datatype` and `is_void` are the shortcuts a transport needs. - Arguments and returns are validated independently at construction: positional only, fully annotated, and one of bool/int/float/str/Enum. Keyword-only arguments are rejected, pending the spike in #403. - `Method` no longer forbids arguments or a return type; `Scan` keeps that restriction for itself. - Transport capability is declared, not assumed. REST and GraphQL round-trip a typed call; Tango carries at most one argument and no enum; EPICS CA and PVA are void-only. Anything a transport cannot serve is skipped with a warning at start-up rather than failing the whole controller, and the EPICS GUI now honours `enabled` so it does not draw a control for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zpFjvbhUisfVUq3NH8M1G
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## refactor #419 +/- ##
============================================
- Coverage 91.25% 91.21% -0.04%
============================================
Files 72 72
Lines 2892 3040 +148
============================================
+ Hits 2639 2773 +134
- Misses 253 267 +14 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`Command[P, T]` renders a reference to a bare `P`, which has no target of its own - the same problem the existing TypeVar entries in nitpick_ignore cover. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zpFjvbhUisfVUq3NH8M1G
The PVA provider, the Tango command collection and the EPICS GUI all now have a branch for a command they cannot serve, and none of them were exercised. The PVA provider builds without a server as long as there is a running loop, and the Tango collection without a device server, so both are reachable here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zpFjvbhUisfVUq3NH8M1G
Closes #393
Lifts the void/void restriction on
Command. A@commandmay now take positional arguments and return a value, both of known types; theinspect.SignaturethatMethodalready captured stops being thrown away and becomes the public description transports read.Scope
Command[P, T]is generic over its parameters and return type (ParamSpec+TypeVar), soCommand[[float], None]is spellable as the ADR writes it.__call__forwards*args/**kwargsinstead of calling a bare zero-arg function, andUnboundCommand.bindcarries that through.[]or[DT1, DT2, …], returns areNoneor a singleDT. There is noAnymiddle case — every argument must be annotated and every type fully known, so a driver that knows nothing statically builds its commands at runtime rather than declaring a half-typed one.*argsand**kwargsare rejected with a message pointing at the keyword-argument spike (SPIKE (Opus/local): typed-command keyword-arg support via TYPE_CHECKING stub #403).Methodno longer forbids arguments or a return type. That restriction was in the shared base only becauseCommandwas the void case; it now lives onScan, which is genuinely void/void, via two small named helpers.Command:signature,argument_types,return_datatype,is_void.Transport capability, declared per transport
{"value": …}, status 200 rather than 204A command a transport cannot serve is skipped with a warning at start-up and the rest of the controller is still served, per the ADR. The EPICS transports have no PV representation of a typed call; Tango commands carry at most one input value, which is a protocol limit rather than a choice.
Instructions to reviewer on how to test:
uv run pytest tests/test_methods.py tests/test_typed_commands.py -v@commandtaking afloaton a controller served over both REST and EPICS CA, and confirm the REST route takes it while the CA IOC logs a skip and still serves the rest.Checks for reviewer
DTypes (bool,int,float,str,Enum) — arrays and tables are deliberately excluded, inCOMMAND_DTYPES. ADR 0015 says command serialisation should be shared withAttributerather than duplicated, and that shared python-type +*Metapath arrives with Remove the DataType family — python types +*Metatyped dicts #413; serving arrays now would mean writing a second, command-specific array serialisation in every transport, which is what the ADR asks us not to do. An array-valued command has an attribute-shaped alternative today, and widening later is additive. Say if you would rather it waited for Remove the DataType family — python types +*Metatyped dicts #413 and served everything.enabled. Previously a command or attribute the CA IOC had skipped still got a widget; with typed commands being skipped routinely that becomes a button that does nothing, soextract_api_componentsskips disabled ones. This also fixes the pre-existing case of an attribute skipped for an over-long PV name.Notes
//DEVICE/stop), because the command path was built with a leading/and then interpolated intof"/{route}"— the attribute path next to it does not. Sub-controller command URLs are now/DEVICE/stop, matching the attributes. Worth a look in case anything downstream depends on the double slash.__signature__as well as__annotations__: strawberry builds a mutation's arguments by introspecting the resolver, and the resolver cannot literally declare parameter names that vary per command._wrap_command_fin the Tango DSR now takes theCommandrather than itsfn, so it can see the signature; likewise the REST and GraphQL wrappers.uv run --locked tox -e pre-commit,type-checking, both green in full. For thetestsenv, this sandbox can't rundocs(needs outbound network) or the PVA/p4p-backed tests (RuntimeError: Address family not supported by protocol), the same known limitation noted on demo: use ControllerVector for temperature ramp sub-controllers #409/demo: cut-down Eiger REST sim + introspectable controller example #410/demo: convert temperature controller to getter/setter style #411/attributes: getter/setter IO rework, remove AttributeIORef/AttributeIO #412. Excluding those,pytest src tests --ignore=tests/benchmarkingpasses 357/367, with only the same 10 pre-existing p4p/socket-family failures, which I confirmed are identical onrefactoritself. Real CI coversdocs, PVA and Tango — note the Tango path is exercised here only through_unservable_reasonunit tests, since this sandbox cannot start a Tango device server.*Metatyped dicts #418 (Remove the DataType family — python types +*Metatyped dicts #413): that one touchesattributes/anddatatypes/, this onemethods/. They overlap only in the transport modules, in different functions.Generated by Claude Code