Refactor Device API to allow binding device's registers - #16
Open
bruno-f-cruz wants to merge 19 commits into
Open
Refactor Device API to allow binding device's registers#16bruno-f-cruz wants to merge 19 commits into
Device API to allow binding device's registers#16bruno-f-cruz wants to merge 19 commits into
Conversation
Narrow the Any-typed inputs of format_bulk / to_buffer / to_file to
PayloadBase | ArrayLike, ArrayLike | None, and MessageType | ArrayLike.
Give Device a base REGISTER_MAP ClassVar default ({}) that generated
subclasses override, and tidy the create_device docstring.
Add a "Generating a Device from a Schema" example walking through `create_device`, including a win/loss discussion of runtime generation vs. pre-generated device packages. Wire it into the examples index and nav, surface `create_device`/`parse_device_schema`/`ConverterContext` in the device API reference, and add a Quickstart to the root README.
bruno-f-cruz
force-pushed
the
refactor-register-binding
branch
from
July 27, 2026 02:13
a31e01e to
7261bbf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10 (register-binding half). Replaces the address-keyed
REGISTER_MAPwith a name-addressable register namespace that works for both runtime-generated (create_device) and statically generated devices, and gives static devices real editor autocomplete and payload-aware types. It also partially addresses a previous concern in #5 (comment) by making registers discoverable from the device itself.What this adds
A device exposes its registers by name through
device.registers, on the class or an instance:__REGISTERS__tuple. The baseDevicemerges in the common Harp registers and derivesdevice.registersin__init_subclass__(the device's register wins on an address clash).RegisterNamespace. Attribute /[name]/[address]access,by_name/by_addressviews, iteration,in,len, and__dir__for REPL discovery. Undeclared attribute access falls back to__getattr__typedtype[RegisterBase[Any]], so any register name still type-checks; aCoreRegistersNamespacesubclass declares specific names for precise types.REGISTER_MAPis gone. No address dict to hand-build, no**_CORE_REGISTER_MAPspread. Address lookups move todevice.registers.by_address.device.registersis read-only. It is backed by a descriptor that defines__get__but no__set__, sodevice.registers = ...is a type error. A read-only member is checked covariantly, which is what lets a subclass narrow it (see below) without an incompatible-override suppression.Static type hints
CoreRegistersNamespaceis a typed facade for the common Harp registers. A statically generated device narrowsdevice.registersby declaring a small nested facade that subclasses it and re-typingregistersto it:With this,
device.registers.AnalogDataautocompletes andread/writeinfer the payload type. Becauseregistersis read-only on the base, the narrowing needs noreportIncompatibleVariableOverridesuppression and noif TYPE_CHECKINGguard — the facade is a plain nested class that is simply never instantiated.Runtime devices (
create_device) get the samedevice.registerssurface for free — without static autocomplete, since the class is built at runtime.What downstream code generators must emit
A statically generated device package emits, per device:
RegisterBasesubclass (unchanged).Devicesubclass that sets only__whoami__(when known) and__REGISTERS__— a tuple of the device's own registers. Do not include the common Harp registers and do not emit aREGISTER_MAP; both are handled by the base.CoreRegistersNamespace, oneName: type[Name]per device register, plusregisters: ClassVar[<Facade>], so editors autocomplete and type-checkdevice.registers.<Name>.The reference shape is tests/device/expected_device.py and the hand-written docs/examples/static_device/static_device.py.
Generators must not: emit
REGISTER_MAP, spread the core registers into__REGISTERS__, or wrap the facade inif TYPE_CHECKING.Dataset reader
harp-data'sDatasetReadersurfaces the same namespace viareader.registers(e.g.reader.registers.by_address), so demultiplexed files can be matched to registers by name or address without a separate map.