From b9518acd2ec982d486ff1110820847e42524a69e Mon Sep 17 00:00:00 2001 From: glopesdev Date: Fri, 21 Aug 2026 13:40:44 +0100 Subject: [PATCH] Review documentation ahead of the release Module-level directives for harp.protocol and harp.device.core give every public name an API page entry, replacing the per-name core directives. The address-call idiom is now documented in the harp-protocol README, showing how a register base with an address reaches a payload no schema names, such as R_UID and R_TAG. The five package READMEs gain the closing license and contribution sentence. tests/test_packaging.py asserts that each published package declares a readme, a license, classifiers, urls and authors, and that every declared license file matches the repository one, since PEP 639 requires a copy per package. --- docs/api/data.md | 1 + docs/api/device.md | 12 +++---- docs/api/protocol.md | 11 +------ src/packages/harp-benchmarks/README.md | 2 ++ src/packages/harp-data/README.md | 2 ++ src/packages/harp-device/README.md | 2 ++ .../src/harp/device/client/_device.py | 2 +- src/packages/harp-protocol/README.md | 16 ++++++++++ src/packages/harp-serial/README.md | 2 ++ tests/test_packaging.py | 31 +++++++++++++++++++ 10 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 tests/test_packaging.py diff --git a/docs/api/data.md b/docs/api/data.md index bc80888..ab15e37 100644 --- a/docs/api/data.md +++ b/docs/api/data.md @@ -3,6 +3,7 @@ --- ::: harp.data.open_dataset +::: harp.data.read ::: harp.data.DatasetReader ::: harp.data.default_file_resolver ::: harp.data.parse_to_dataframe diff --git a/docs/api/device.md b/docs/api/device.md index 3fd3865..8dc7c0a 100644 --- a/docs/api/device.md +++ b/docs/api/device.md @@ -4,16 +4,14 @@ ::: harp.device.client.Device ::: harp.device.client.DeviceError +::: harp.device.client.Subscription +::: harp.device.client.EventHandler ::: harp.device.client.HarpFramer ::: harp.device.client.ITransport ::: harp.device.client.TransportError ::: harp.device.schema.create_device_module ::: harp.device.schema.parse_device_schema ::: harp.device.schema.ConverterContext -::: harp.device.core.REGISTER_MAP -::: harp.device.core.OperationControl -::: harp.device.core.OperationMode -::: harp.device.core.ResetDevice -::: harp.device.core.ResetFlags -::: harp.device.core.ClockConfiguration -::: harp.device.core.ClockConfigurationFlags +::: harp.device.schema.DeviceModule +::: harp.device.schema.DeviceModuleLike +::: harp.device.core diff --git a/docs/api/protocol.md b/docs/api/protocol.md index 0a43ce2..b392e59 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -2,13 +2,4 @@ --- -::: harp.protocol.MessageType -::: harp.protocol.PayloadType -::: harp.protocol.HarpMessage -::: harp.protocol.RegisterBase -::: harp.protocol.StructPayload -::: harp.protocol.AnonymousPayload -::: harp.protocol.Field -::: harp.protocol.GroupMask -::: harp.protocol.BitMask -::: harp.protocol.Converter +::: harp.protocol \ No newline at end of file diff --git a/src/packages/harp-benchmarks/README.md b/src/packages/harp-benchmarks/README.md index a772d2c..fd642fa 100644 --- a/src/packages/harp-benchmarks/README.md +++ b/src/packages/harp-benchmarks/README.md @@ -45,3 +45,5 @@ Equivalent module invocations: `uv run python -m harp.benchmarks.benchmark` / `u - **re-read**, file re-read from disk on every run, the real-world "load a dump" path, which includes disk. The report also decomposes `parse_to_dataframe` into `parse_bulk` plus `payload_as_columns` plus pandas overhead. + +`harp-benchmarks` is released as open source under the [MIT license](https://github.com/harp-tech/python/blob/main/LICENSE). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/harp-tech/python). diff --git a/src/packages/harp-data/README.md b/src/packages/harp-data/README.md index 2d4f9dc..05758c8 100644 --- a/src/packages/harp-data/README.md +++ b/src/packages/harp-data/README.md @@ -95,3 +95,5 @@ from harp import data data.to_file(AnalogData, values, "AnalogData.bin", timestamps=seconds) ``` + +`harp-data` is released as open source under the [MIT license](https://github.com/harp-tech/python/blob/main/LICENSE). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/harp-tech/python). diff --git a/src/packages/harp-device/README.md b/src/packages/harp-device/README.md index 1da8d6b..a1a12ac 100644 --- a/src/packages/harp-device/README.md +++ b/src/packages/harp-device/README.md @@ -70,3 +70,5 @@ schema.create_device_module(yml_text, converters={"DataConverter": DataConverter ``` `parse_device_schema(yml_text)` is also public, returning the parsed schema model without a module: registers, masks, and optional device identity. + +`harp-device` is released as open source under the [MIT license](https://github.com/harp-tech/python/blob/main/LICENSE). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/harp-tech/python). diff --git a/src/packages/harp-device/src/harp/device/client/_device.py b/src/packages/harp-device/src/harp/device/client/_device.py index 151425d..90b0c3c 100644 --- a/src/packages/harp-device/src/harp/device/client/_device.py +++ b/src/packages/harp-device/src/harp/device/client/_device.py @@ -324,7 +324,7 @@ def _remove_subscription(self, sub: "Subscription") -> None: def _event_loop(self) -> None: while True: msg = self._event_queue.get() - if msg is None: # shutdown sentinel + if msg is None: # shutdown marker break self._deliver_event(msg) diff --git a/src/packages/harp-protocol/README.md b/src/packages/harp-protocol/README.md index ea24445..86768e2 100644 --- a/src/packages/harp-protocol/README.md +++ b/src/packages/harp-protocol/README.md @@ -30,4 +30,20 @@ np.uint16(65535) + 1 # RuntimeWarning: overflow encountered in scalar add Numpy scalars behave like plain Python numbers in arithmetic, comparison and formatting. Use `int()` or `float()` where a built-in type is required. +## Reading an address no schema describes + +A register class is normally declared with its address, as above, or generated from a `device.yml`. Calling a register base with an address instead builds a one-off register for that address, which is how a payload outside any schema is read and written: + +```python +from harp.protocol import RegisterU8Array, RegisterU16 + +uid = RegisterU8Array(0x10, length=16) # R_UID, named by no schema +tag = RegisterU8Array(0x11, length=16) # R_TAG, the firmware git hash +version = RegisterU16(0x08) # any address, as a scalar +``` + +The result is an ordinary register, so it goes through `read` and `write` on a device exactly as a declared one does. `length` is keyword-only for the array form, and it is the element count rather than a byte count. An already-addressed register rejects the call, so `WhoAmI(44)` raises rather than quietly producing a register at another address. + It carries no transport or device logic. See [`harp-device`](https://github.com/harp-tech/python/tree/main/src/packages/harp-device) for the device layer. + +`harp-protocol` is released as open source under the [MIT license](https://github.com/harp-tech/python/blob/main/LICENSE). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/harp-tech/python). diff --git a/src/packages/harp-serial/README.md b/src/packages/harp-serial/README.md index 0b55f19..0036f75 100644 --- a/src/packages/harp-serial/README.md +++ b/src/packages/harp-serial/README.md @@ -17,3 +17,5 @@ with serial.open_device(behavior, port="COM3") as device: ``` Passing a device module validates the device identity on open. Pass a `Device` subclass instead to preserve its own type, or omit the argument entirely for schema-free access, which skips the identity check. + +`harp-serial` is released as open source under the [MIT license](https://github.com/harp-tech/python/blob/main/LICENSE). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/harp-tech/python). diff --git a/tests/test_packaging.py b/tests/test_packaging.py new file mode 100644 index 0000000..7acd1da --- /dev/null +++ b/tests/test_packaging.py @@ -0,0 +1,31 @@ +import tomllib +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +PUBLISHED = ("harp-protocol", "harp-device", "harp-serial", "harp-data") + + +def _manifest(package: str) -> dict: + path = ROOT / "src" / "packages" / package / "pyproject.toml" + return tomllib.loads(path.read_text(encoding="utf-8"))["project"] + + +@pytest.mark.parametrize("package", PUBLISHED) +def test_declared_license_files_match_repository_license(package: str): + # PEP 639 forbids a parent directory reference in license-files, so each package + # keeps its own copy. Nothing stops the copies drifting except this. + expected = (ROOT / "LICENSE").read_bytes() + declared = _manifest(package)["license-files"] + assert declared, f"{package} declares no license file" + for name in declared: + assert (ROOT / "src" / "packages" / package / name).read_bytes() == expected + + +@pytest.mark.parametrize("package", PUBLISHED) +def test_published_package_declares_metadata(package: str): + # A published package with no readme or license renders as a blank project page. + project = _manifest(package) + for key in ("readme", "license", "license-files", "classifiers", "urls", "authors"): + assert key in project, f"{package} declares no {key}"