Skip to content

utils.configuration_json: hold the configuration JSON walk once, out of the STAR driver - #1280

Open
BioCam wants to merge 4 commits into
PyLabRobot:mainfrom
BioCam:utils-configuration-json
Open

BioCam wants to merge 4 commits into
PyLabRobot:mainfrom
BioCam:utils-configuration-json

Conversation

@BioCam

@BioCam BioCam commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Reading a saved configuration back needs more than json.load: JSON loses a tuple, a dict's key type and a date, and what each field is declared to hold is what puts all three back. That walk, and the to_jsonable that writes it, are defined on the STAR driver's configuration module, so a driver that wants to save and load its own configurations has to import from the STAR to get them.

They move to pylabrobot.utils.configuration_json, unchanged.

  • to_jsonable, and the walk that reads a value back against the type its field declares: Optional, fixed- and variable-length tuples, lists, dicts including the int keys JSON writes as text, dates, and nested dataclasses. A name the class no longer has is left out, so a file written by a driver that has since dropped a field still loads.
  • star/driver/configuration.py loses the two definitions and reads with the shared ones instead: 79 lines out, 2 in.
  • The five places that wrote with to_jsonable, star/driver/master.py and four test modules, import it from its new home.

Behaviour: the same walk over the same declared types. Nothing is written or read differently, and no configuration file that loaded before stops loading.

Tests: configuration_json_tests.py comes across with the module. The full suite passes (3410 passed, 4 skipped, 952 subtests), under pytest 9.0.3 and 9.1.1 both, since the two differ on caplog. make lint, make format-check and typos are clean, and every tracked source parses under Python 3.9. make typecheck reports only the nine errors in li_cor/odyssey/odyssey_tests.py that are already on main, in a file this branch does not touch.

🤖 Generated with Claude Code

…r than the STAR's own

Reading a saved configuration back needs more than `json.load`: JSON loses a
tuple, a dict's key type and a date, and what each field is declared to hold is
what puts all three back. That walk, and the `to_jsonable` that writes it, were
defined on the STAR driver's configuration module, so any other driver wanting
to save and load its own configurations had to import from the STAR to get
them.

They move to `pylabrobot.utils.configuration_json` unchanged. The STAR's
configuration module reads with them as before, and the five places that wrote
with `to_jsonable` import it from its new home.

No behaviour changes: the same walk, over the same declared types.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BioCam
BioCam requested a review from a team as a code owner September 18, 2026 20:34
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment on lines +13 to +34
def to_jsonable(value: Any) -> Any:
"""The value as JSON holds it.

Args:
value: what to convert - a configuration, or anything one holds.

Returns:
The same value in types `json.dump` accepts.
"""
if dataclasses.is_dataclass(value) and not isinstance(value, type):
return {
field.name: to_jsonable(getattr(value, field.name)) for field in dataclasses.fields(value)
}
if isinstance(value, datetime.date):
return value.isoformat()
if isinstance(value, (list, tuple)):
return [to_jsonable(item) for item in value]
if isinstance(value, dict):
# Keys are written as text because JSON has no other kind. What they were is on the field.
return {str(key): to_jsonable(item) for key, item in value.items()}
return value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we already have a serialize function for this, why not just use that?

@rickwierenga

Copy link
Copy Markdown
Member

this entire PR is duplicating the serializer instead of extending it with datetime

@BioCam BioCam changed the title Share the configuration JSON walk as pylabrobot.utils.configuration_json utils.configuration_json: hold the configuration JSON walk once, out of the STAR driver Sep 18, 2026
The changelog is written at the next release, not per change. What this
moves, and that `to_jsonable` is imported from a new home, belongs in the
PR description.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BioCam

BioCam commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

@rickwierenga - I think it's a different job. deserialize needs a "type" key to know what to build, and a saved configuration has none - the five recordings in star/driver/recordings have zero. Run it on one and the dict comes straight back: x_range still a list, firmware_date still '2021-11-05'. serialize doesn't get that far - TypeError: vars() argument must have __dict__ attribute on the date. It's 49 tuple fields and 4 date ones.

Happy to fold it into serializer.py though: deserialize given the type it reads into, beside the tag-driven one, and utils.configuration_json gone. Prefer that?

@rickwierenga

Copy link
Copy Markdown
Member

it needs a type for classes, not for ordinary data. the job is exactly the same, turn python data into json-compatible dicts and back

this diff should be approx 4 lines in serializer.py

@BioCam

BioCam commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

I tried it: the write side is 3 lines, but the read side isn't. head_types is Dict[int, str] and json writes those keys as text, so head_types.get(1) comes back None where head.py:473 reads it - every head silently "unknown", with no error. firmware_date.year at head96.py:113 gives AttributeError: 'str' object has no attribute 'year'.

Neither can come back from the json alone - "2021-11-05" and "1" are just strings unless something declares what the field holds. serialize also writes a "type" key that the five recordings don't have. I'm happy to put the type-directed read into serializer.py and drop utils.configuration_json - shall I?

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.

2 participants