refactor: extract shared state_store; route usb_power through it#45
Merged
Conversation
usb_power persisted its (hub, port) cache by serializing just those keys and replacing the whole file — fine as the sole writer, but it would clobber any other feature's slice. Extract state_store.py as the single owner of LABELLE_STATE_FILE: atomic read-modify-write of the whole document under a process-wide lock, best-effort (swallows I/O and serialization errors), tolerant of a missing/corrupt/non-UTF8 file. Route usb_power's load/save through it (same on-disk shape, same path arg; also warns on a present-but-invalid hub/port shape). Behavior-preserving for usb_power; sets up safe multi-writer sharing for the upcoming per-printer settings (#39). A smoke guard asserts state_store is the only module referencing LABELLE_STATE_FILE. No version bump — ships in v1.8.0 with #39, which builds on this. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared, atomic read-modify-write JSON state store (LABELLE_STATE_FILE) so multiple backend features can persist independent slices of state without clobbering each other (e.g., USB power’s (hub, port) cache alongside upcoming per-printer settings).
Changes:
- Added
server/state_store.pyas the single owner ofLABELLE_STATE_FILE, providing best-effort atomic read/modify/write under a process-wide lock. - Refactored
usb_powerto load/save itshub/portviastate_storeinstead of overwriting the entire state file. - Added/updated tests covering state_store behavior (corrupt/non-UTF8/serialization/concurrency) and a smoke guard ensuring only
state_store.pyreferencesLABELLE_STATE_FILE.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
server/state_store.py |
New shared state file owner implementing atomic, locked RMW and tolerant reads/writes. |
server/usb_power.py |
Routes hub/port persistence through state_store and refines logging for invalid shapes. |
server/tests/test_state_store.py |
New tests for atomic RMW, key preservation, error swallowing, and concurrency. |
server/tests/test_usb_power.py |
Updates tests for state_store delegation and adds warnings/quietness assertions. |
server/tests/test_smoke.py |
Adds ownership smoke test to prevent direct LABELLE_STATE_FILE usage outside state_store. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Path.read_text/write_text default to the process locale encoding, which can vary between environments and make the persisted JSON unreadable across restarts if the locale changes. Use encoding="utf-8" on both sides. Also correct the usb_power comment describing what state_store actually logs. Adds a non-ASCII round-trip test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
szrudi
added a commit
that referenced
this pull request
Jun 13, 2026
Brings in #41 (serial ids), #42 (virtual ids), #44 (default printer selection), #45 (shared state_store + UTF-8). Conflict resolution: - state_store.py / usb_power.py / test_state_store.py: took main's (the #45 versions with UTF-8 read/write + corrected comment + non-ASCII test) — they supersede #39's older extracted copies. - test_smoke.py: kept #39's (superset — adds printer_settings module and the /api/printers/<id>/settings route on top of #45's ownership guard). - useLabelStore setAvailablePrinters: combined main's default-printer selection with #39's availablePrintersLoaded flag. - SettingsBar + its tests, useLabelStore tests, ARCHITECTURE.md: unioned #44's selector changes with #39's persist/load-gate. Frontend 81, backend 276, build green.
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.
Infrastructure split out of #39 so that PR stays focused on the feature. No version bump — ships in the single v1.8.0 with #39, which builds on this.
Why
usb_powerpersisted its(hub, port)cache by serializing just those two keys and replacing the whole state file. That's an atomic write but a full-file overwrite — fine whileusb_poweris the only writer, but it would clobber any other feature's slice of the same file.What
server/state_store.py— the single owner ofLABELLE_STATE_FILE:usb_powernow routes its load/save through it — same on-disk shape (hub/portat top level), samepathargument, behavior-preserving (its tests still pass). It also now warns on a present-but-invalid hub/port shape.A smoke test guards the invariant: only
state_store.pymay referenceLABELLE_STATE_FILE, so no feature reintroduces direct (clobbering) access.Why now
This is the shared-file foundation the upcoming per-printer settings (#39) builds on. Landing it separately keeps #39 to just the feature.
Tests
New
test_state_store.py(atomic RMW, key preservation, concurrency, corrupt/non-UTF8/serialization handling);test_usb_powerupdated for the delegation + invalid-shape warning; smoke ownership guard. Full backend suite green (252).🤖 Generated with Claude Code