docs(02-01): complete INF parser plan
- 02-01-SUMMARY.md: TDD execution summary with deviation notes - STATE.md: updated position, decisions, metrics, session - ROADMAP.md: phase 2 progress updated (1/2 summaries) - REQUIREMENTS.md: DRV-02 and DRV-05 marked complete
This commit is contained in:
@@ -10,10 +10,10 @@ Requirements for initial release. Each maps to roadmap phases.
|
|||||||
### Driver Management
|
### Driver Management
|
||||||
|
|
||||||
- [ ] **DRV-01**: User can upload a driver package (ZIP containing INF + supporting files)
|
- [ ] **DRV-01**: User can upload a driver package (ZIP containing INF + supporting files)
|
||||||
- [ ] **DRV-02**: System parses uploaded INF files and extracts valid driver names (DriverDesc)
|
- [x] **DRV-02**: System parses uploaded INF files and extracts valid driver names (DriverDesc)
|
||||||
- [ ] **DRV-03**: User can select driver name from parsed INF dropdown (no free-text)
|
- [ ] **DRV-03**: User can select driver name from parsed INF dropdown (no free-text)
|
||||||
- [ ] **DRV-04**: Driver packages are persisted on Docker volume across container restarts
|
- [ ] **DRV-04**: Driver packages are persisted on Docker volume across container restarts
|
||||||
- [ ] **DRV-05**: System flags unused files in driver packages to help reduce package size
|
- [x] **DRV-05**: System flags unused files in driver packages to help reduce package size
|
||||||
|
|
||||||
### Printer Configuration
|
### Printer Configuration
|
||||||
|
|
||||||
@@ -86,10 +86,10 @@ Which phases cover which requirements. Updated during roadmap creation.
|
|||||||
| INFRA-01 | Phase 1 | Complete |
|
| INFRA-01 | Phase 1 | Complete |
|
||||||
| INFRA-02 | Phase 1 | Complete |
|
| INFRA-02 | Phase 1 | Complete |
|
||||||
| DRV-01 | Phase 2 | Pending |
|
| DRV-01 | Phase 2 | Pending |
|
||||||
| DRV-02 | Phase 2 | Pending |
|
| DRV-02 | Phase 2 | Complete |
|
||||||
| DRV-03 | Phase 2 | Pending |
|
| DRV-03 | Phase 2 | Pending |
|
||||||
| DRV-04 | Phase 2 | Pending |
|
| DRV-04 | Phase 2 | Pending |
|
||||||
| DRV-05 | Phase 2 | Pending |
|
| DRV-05 | Phase 2 | Complete |
|
||||||
| PRNT-01 | Phase 3 | Pending |
|
| PRNT-01 | Phase 3 | Pending |
|
||||||
| PRNT-02 | Phase 3 | Pending |
|
| PRNT-02 | Phase 3 | Pending |
|
||||||
| PRNT-03 | Phase 3 | Pending |
|
| PRNT-03 | Phase 3 | Pending |
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5
|
|||||||
| Phase | Plans Complete | Status | Completed |
|
| Phase | Plans Complete | Status | Completed |
|
||||||
|-------|----------------|--------|-----------|
|
|-------|----------------|--------|-----------|
|
||||||
| 1. Foundation | 3/3 | Complete | 2026-04-10 |
|
| 1. Foundation | 3/3 | Complete | 2026-04-10 |
|
||||||
| 2. Driver Management | 0/2 | Not started | - |
|
| 2. Driver Management | 1/2 | In Progress| |
|
||||||
| 3. Printer Configuration | 0/3 | Not started | - |
|
| 3. Printer Configuration | 0/3 | Not started | - |
|
||||||
| 4. Script Generation | 0/3 | Not started | - |
|
| 4. Script Generation | 0/3 | Not started | - |
|
||||||
| 5. Package Export | 0/3 | Not started | - |
|
| 5. Package Export | 0/3 | Not started | - |
|
||||||
|
|||||||
+9
-6
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 01-02-PLAN.md
|
stopped_at: Completed 02-01-PLAN.md
|
||||||
last_updated: "2026-04-10T09:36:14.222Z"
|
last_updated: "2026-04-10T09:57:42.006Z"
|
||||||
last_activity: "2026-04-10 — Plan 01-01 complete: Docker scaffold, FastAPI app shell, sidebar templates, 3-test green suite"
|
last_activity: "2026-04-10 — Plan 01-01 complete: Docker scaffold, FastAPI app shell, sidebar templates, 3-test green suite"
|
||||||
progress:
|
progress:
|
||||||
total_phases: 5
|
total_phases: 5
|
||||||
completed_phases: 1
|
completed_phases: 1
|
||||||
total_plans: 3
|
total_plans: 5
|
||||||
completed_plans: 3
|
completed_plans: 4
|
||||||
percent: 7
|
percent: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ Progress: [░░░░░░░░░░] 7%
|
|||||||
|
|
||||||
*Updated after each plan completion*
|
*Updated after each plan completion*
|
||||||
| Phase 01-foundation P01-02 | 3 | 2 tasks | 7 files |
|
| Phase 01-foundation P01-02 | 3 | 2 tasks | 7 files |
|
||||||
|
| Phase 02 P01 | 3 | 1 tasks | 6 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -68,6 +69,8 @@ Recent decisions affecting current work:
|
|||||||
- [Phase 01-foundation]: Deferred SqliteDatabase(None) pattern so tests can patch DB_PATH via monkeypatch without module reload
|
- [Phase 01-foundation]: Deferred SqliteDatabase(None) pattern so tests can patch DB_PATH via monkeypatch without module reload
|
||||||
- [Phase 01-foundation]: Full 4-table schema created upfront in phase 1 — later phases add routes/logic only, no schema changes
|
- [Phase 01-foundation]: Full 4-table schema created upfront in phase 1 — later phases add routes/logic only, no schema changes
|
||||||
- [Phase 01-foundation]: init_db() added to lifespan (not @app.on_event) consistent with 01-01 established pattern
|
- [Phase 01-foundation]: init_db() added to lifespan (not @app.on_event) consistent with 01-01 established pattern
|
||||||
|
- [Phase 02]: optionxform=str on RawConfigParser preserves DriverDesc key casing; strings dict uses lowercase keys for case-insensitive token lookup
|
||||||
|
- [Phase 02]: RawConfigParser(strict=False) required for real INFs that have duplicate option keys within sections
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -80,6 +83,6 @@ None yet.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-10T09:33:07.637Z
|
Last session: 2026-04-10T09:57:42.003Z
|
||||||
Stopped at: Completed 01-02-PLAN.md
|
Stopped at: Completed 02-01-PLAN.md
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
phase: "02"
|
||||||
|
plan: "01"
|
||||||
|
subsystem: inf-parser
|
||||||
|
tags: [tdd, inf-parsing, encoding-detection, token-resolution, driver-management]
|
||||||
|
dependency_graph:
|
||||||
|
requires: []
|
||||||
|
provides: [inf-parser-service]
|
||||||
|
affects: [02-02-upload-endpoint, 02-03-drivers-ui]
|
||||||
|
tech_stack:
|
||||||
|
added: []
|
||||||
|
patterns: [RawConfigParser-strict-false, BOM-sniffing, optionxform-str, set-dedup-sorted]
|
||||||
|
key_files:
|
||||||
|
created:
|
||||||
|
- imptune/services/__init__.py
|
||||||
|
- imptune/services/inf_parser.py
|
||||||
|
- tests/test_inf_parser.py
|
||||||
|
- tests/fixtures/sample.inf
|
||||||
|
- tests/fixtures/sample_utf16.inf
|
||||||
|
- tests/fixtures/sample_multi_model.inf
|
||||||
|
modified: []
|
||||||
|
decisions:
|
||||||
|
- "optionxform=str on RawConfigParser to preserve DriverDesc key casing; strings dict still uses lowercased keys for case-insensitive %TOKEN% lookup"
|
||||||
|
- "configparser.RawConfigParser(strict=False) avoids DuplicateOptionError on real INFs with repeated model entries"
|
||||||
|
- "UTF-16 fixture written as binary via Python encode('utf-16') — not as a text file — to guarantee correct BOM bytes"
|
||||||
|
metrics:
|
||||||
|
duration: "~2.5 min"
|
||||||
|
completed: "2026-04-10"
|
||||||
|
tasks: 1
|
||||||
|
files: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 02 Plan 01: INF Parser Service Summary
|
||||||
|
|
||||||
|
**One-liner:** stdlib configparser + BOM-sniffing INF parser with %TOKEN% resolution, multi-model deduplication, architecture detection, and unused-file flagging.
|
||||||
|
|
||||||
|
## What Was Built
|
||||||
|
|
||||||
|
`imptune/services/inf_parser.py` — a pure-function INF parser with:
|
||||||
|
|
||||||
|
- `ParsedInf` dataclass exposing `driver_names`, `inf_filename`, `architecture`, `has_cat_file`, `unused_files`
|
||||||
|
- `_detect_encoding(raw: bytes) -> str` — BOM-sniffing: `\xff\xfe`/`\xfe\xff` -> `utf-16`, `\xef\xbb\xbf` -> `utf-8-sig`, else `cp1252`
|
||||||
|
- `_resolve_tokens(value, strings)` — regex `%([^%]+)%` expansion
|
||||||
|
- `parse_inf(inf_text, inf_filename, zip_names) -> ParsedInf` — `RawConfigParser(strict=False, delimiters=('=',))` with `optionxform=str`; [Manufacturer] -> Models section discovery; NTamd64/NTarm64/NTx86/undecorated detection; set-based dedup; sorted output
|
||||||
|
|
||||||
|
Three fixture files support the test suite: `sample.inf` (ANSI with %TOKEN%), `sample_utf16.inf` (UTF-16 LE BOM binary), `sample_multi_model.inf` (NTamd64 + undecorated sections).
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
| # | Task | Status | Commit |
|
||||||
|
|---|------|--------|--------|
|
||||||
|
| 1 | INF parser with TDD (RED then GREEN) | Complete | 290106d (RED), 5056922 (GREEN) |
|
||||||
|
|
||||||
|
## Test Results
|
||||||
|
|
||||||
|
- 16 tests in `tests/test_inf_parser.py` — all pass
|
||||||
|
- Full suite: 40 tests pass, 0 failures, 0 regressions
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 1 - Bug] configparser key lowercasing mangled DriverDesc literal names**
|
||||||
|
- **Found during:** Task 1, GREEN phase (first test run)
|
||||||
|
- **Issue:** configparser defaults `optionxform = str.lower`, so the literal key `Acme SuperPrint 9000` was returned as `acme superprint 9000`. The test `assert "Acme SuperPrint 9000" in result.driver_names` failed.
|
||||||
|
- **Fix:** Set `parser.optionxform = str` to preserve original casing of option keys. The [Strings] dict still explicitly lowercases keys (`strings[key.lower()]`) for case-insensitive token resolution.
|
||||||
|
- **Files modified:** `imptune/services/inf_parser.py`
|
||||||
|
- **Commit:** 5056922
|
||||||
|
|
||||||
|
**Note:** The plan specified `strict=False` and `RawConfigParser` correctly but did not mention `optionxform=str`. This is a real-INF edge case documented in the pitfalls section of 02-RESEARCH.md (implicitly — the note says "Strings dict keys must be lowercased" without clarifying that DriverDesc keys also get lowercased by default).
|
||||||
|
|
||||||
|
### Test Count Deviation
|
||||||
|
|
||||||
|
The plan specified 11 test functions; 16 were written. The extra 5 cover:
|
||||||
|
- `test_detect_encoding_utf16be` (UTF-16 BE BOM variant)
|
||||||
|
- `test_architecture_detection_amd64` (split from the combined architecture test)
|
||||||
|
- `test_architecture_detection_arm64`
|
||||||
|
- `test_architecture_detection_undecorated`
|
||||||
|
- `test_architecture_detection_mixed`
|
||||||
|
|
||||||
|
This provides more granular failure diagnosis and meets the `min_lines: 80` artifact requirement.
|
||||||
|
|
||||||
|
## Self-Check
|
||||||
|
|
||||||
|
- [x] `imptune/services/inf_parser.py` exists
|
||||||
|
- [x] `imptune/services/__init__.py` exists
|
||||||
|
- [x] `tests/test_inf_parser.py` exists (>80 lines)
|
||||||
|
- [x] `tests/fixtures/sample.inf` exists
|
||||||
|
- [x] `tests/fixtures/sample_utf16.inf` exists (UTF-16 LE BOM binary)
|
||||||
|
- [x] `tests/fixtures/sample_multi_model.inf` exists
|
||||||
|
- [x] RED commit: 290106d
|
||||||
|
- [x] GREEN commit: 5056922
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
Reference in New Issue
Block a user