Add ArrayConverter for array payload members - #39
Merged
Conversation
glopesdev
force-pushed
the
fix-array-member-typing
branch
from
August 22, 2026 19:43
2b1bf53 to
28b7143
Compare
A payload member spanning several elements had no expressible type. The sub-array dtype IdentityConverter needs carries np.void as its scalar type, so the member resolved as void, and pinning the type parameter instead resolves it as the scalar. ArrayConverter takes the element type and a length and resolves as an NDArray of that element. It is typing-only and builds an IdentityConverter over the same sub-array dtype, so the runtime object is the passthrough converter every code path and isinstance already expects. The runtime emitter uses it for a member longer than one element, so a schema-built module and a generated package agree. harp-benchmarks was the one package src tree outside the pyright include, and no test imports its benchmark module, so three parse_to_dataframe calls still passed timestamp and raised TypeError. They now pass time_index, a masked np.int32 member no longer takes a Python int, and the StartPulseTrain default check supplies the two members that dataclass_transform makes required.
glopesdev
force-pushed
the
fix-array-member-typing
branch
from
August 22, 2026 19:57
28b7143 to
514c4d9
Compare
bruno-f-cruz
approved these changes
Aug 22, 2026
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.
A payload member spanning several elements of one type could not be given an honest annotation.
ArrayConvertercloses that, and the harp-benchmarks package that exercised the gap is brought under pyright so the next drift is caught by CI.The member that could not be typed
A
payloadSpecmember declaring its ownlengthis emitted as a passthrough over a sub-array dtype:pyright rejects the assignment with
Type "void" is not assignable to declared type "NDArray[float32]", because numpy types a sub-array dtype asdtype[void], so the converter type parameter binds tonp.void.Both obvious repairs were measured on a probe and both fail:
IdentityConverter[np.float32](np.dtype((np.float32, (3,)))), pinning the parameter explicitly, yieldsField[np.float32], and a scalar is not assignable to an array eithernp.dtype[np.float32]before passing it does the same thing for the same reasonSo the element type and the length have to arrive as separate arguments, because a sub-array dtype has already discarded the element type by the time any overload could inspect it.
ArrayConverter(np.float32, 3)does that and resolves asNDArray[np.float32].Nothing about
Fieldchanges.Converteris generic in its decoded type with no bound, anddecode_batchalready returnsAny, so an array-valued converter was expressible all along.StringConverteris the existing precedent: it takes a length, builds the identical sub-array dtype, decodes to a non-scalar, and appears in generated output today ascore_id: str = Field(StringConverter(3), offset=9).Why it routes through IdentityConverter at runtime
ArrayConverteris declared underTYPE_CHECKINGand is a factory outside it, returning anIdentityConverterover the equivalent sub-array dtype. The runtime object is therefore the passthrough converter every existing code path already expects, and_payload.pyhas a zero-line delta.Preserving the runtime identity is important. Sub-array column expansion, the step that renders
accelerometerasaccelerometer_0,accelerometer_1andaccelerometer_2, is selected by anisinstancecheck againstIdentityConverter. A genuinely separate converter class would fail that check, taking the whole-element branch, and handing pandas a 2D column, which would end up raisingValueError: Per-column arrays must each be 1-dimensional. Routing to the same runtime class avoids introducing that divergence at all.Measured after the change: the constructed object is an
IdentityConverter, its dtype equals the direct spelling at itemsize 12, all fourteen registers round-trip, andharp-benchmark --headstill produces six columns forAnalogData.Checking harp-benchmarks
harp-benchmarks/srcwas the one packagesrctree absent from the pyrightinclude, and no test imports its benchmark module, onlyregister_models. Threeparse_to_dataframecalls would still have passedtimestampand raisedTypeErrorat runtime; they now passtime_index. Two further errors were found in that package: a maskednp.int32member was given a Pythonint, and theStartPulseTraindefault check omitted two members that@dataclass_transformmakes required. Adding the tree to the include will prevent future drift.The
tests/conformance.pyfixture also enforces the typed contract. If a change causes an array member to revert to a scalar value without warning, the build process will fail.What this does not change
tests/device/expected_device.pykeeps its two array errors, because it is a byte copy of generator output and resyncs by copying rather than by hand. It sits outside the pyrightinclude, so CI is unaffected. Once the Python generator target emitsArrayConverter, those should clear.