{ "intent": "Bring brother_printer/cups_service.py under the 250-line cap by moving the IPP queries, status-code mapping and CUPS-based USB fallback into their own module, with no behaviour change.", "scope": [ "python_pkg/brother_printer/_cups_fallback.py (new)", "python_pkg/brother_printer/cups_service.py", "python_pkg/brother_printer/cups_queue.py, usb_query.py (import path only)", "python_pkg/brother_printer/tests/test_cups_service_part2.py, part3, part4 (MOD and import path only)", "python_pkg/brother_printer/tests/test_usb_query.py (one absolute patch target)", "Non-goal: no assertion changed, no test deleted, skipped or weakened" ], "changes": [ "Moved _parse_ipp_attributes, _get_cups_ipp_status, _get_cups_economode, _map_cups_to_status_code, _cups_reasons_to_error, _port_status_to_status_code, find_cups_printer_name and query_usb_via_cups verbatim into python_pkg/brother_printer/_cups_fallback.py.", "The seam is the whole IPP-onwards section rather than the section header at line 237: test_cups_service_part2.py imports both _get_cups_economode and find_cups_printer_name and patches shutil once, so those two had to travel together or that module would need two MOD constants.", "_cups_fallback imports _ensure_cups_running, _get_pyusb_device_info and _query_usb_port_status_raw from cups_service, so test_cups_service_part3.py can patch all five of its targets on one module.", "cups_service.py dropped the imports only the moved code used (re, printer_info_from_cups, run_command_text, estimate_consumable_life, USBResult, the three CUPS mapping tables and the three DERIVED_* codes) and its docstring now names the new module; 406 -> 234 lines.", "cups_queue.py imports find_cups_printer_name from _cups_fallback, and usb_query.py's late importlib.import_module now names _cups_fallback. test_usb_query.py patches that path absolutely, so its one patch string moved with it." ], "verification": [ { "command": "PYTHONPATH=. python3 -m pytest python_pkg/brother_printer/tests -q", "result": "pass", "evidence": "402 passed before the split and 402 passed after it - identical pass count, not merely a green run." }, { "command": "PYTHONPATH=. python3 -c \"from python_pkg.brother_printer import _cups_fallback as q, cups_service as c, cups_queue as cq; q._ensure_cups_running; q._query_usb_port_status_raw; q.find_cups_printer_name; q.shutil; q.subprocess; c.fcntl; c.os; c.Path; c.time; cq.find_cups_printer_name\"", "result": "pass", "evidence": "runtime probe OK - every name the four test modules patch is still a real attribute of the module they name, so ruff's TC001 rewrite trap did not fire." }, { "command": "wc -l python_pkg/brother_printer/cups_service.py python_pkg/brother_printer/_cups_fallback.py", "result": "pass", "evidence": "234 and 209 lines, both under the 250-line cap in a single cut." }, { "command": "pre-commit run --files ", "result": "pass", "evidence": "ruff, ruff format, mypy, pylint, bandit and pytest-with-coverage all pass on the touched files." } ], "risks": [ "usb_query.py reaches the fallback through a late importlib.import_module with the module path as a string, which no import checker validates. Covered by test_usb_query.py exercising that path, and by the pass count staying at 402 - a stale string would have raised ModuleNotFoundError there.", "cups_queue -> _cups_fallback -> cups_service is now a three-module chain; moving anything else out of cups_service must keep that direction to avoid a cycle." ], "rollback": [ "git revert the commit; cups_service.py returns to 406 lines and _cups_fallback.py disappears.", "After rollback, re-run the package suite and confirm it still reports 402 passed." ] }