mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 21:47:02 +01:00
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint were still grading `meta/` against 3.10 -- three tools judging an interpreter nothing runs any more, and ruff withholding the pyupgrade fixes that only become safe at the newer target. `pin_ci_python.py` rewrites these three knobs. What the stale target was hiding: `datetime.timezone.utc` where `datetime.UTC` is now correct (UP017), and ruff's stable formatter applying PEP 758 -- `except (A, B):` becomes `except A, B:`. That makes the source 3.14-only at the syntax level, which is what `requires-python = ">=3.14"` already promised; kuhy chose it deliberately over holding ruff a release back (2026-08-29). Also drops a comment in `wsg_grabber/models.py` that the bump makes false: it explained why the file used ``str, Enum`` instead of ``StrEnum`` under mypy at 3.10, sitting directly above a ``class Verdict(StrEnum)`` that had already stopped obeying it. ruff, ruff-format, mypy --strict and pylint all clean at the new target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""Shared subprocess and CUPS-query helpers for the brother_printer package.
|
|
|
|
Centralises the short, non-checking command invocation and the ``lpstat``-based
|
|
USB-info parsing that the CUPS, USB, and status modules all repeat, so the
|
|
subprocess boilerplate and URI parsing live in exactly one place.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import subprocess
|
|
import urllib.parse
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def run_command_text(args: list[str], *, timeout: float = 5) -> str:
|
|
"""Run ``args`` and return its captured stdout, or "" on any failure.
|
|
|
|
A non-checking run with captured text output and a short timeout. Any
|
|
timeout, subprocess, or OS error is swallowed and reported as empty output,
|
|
so callers can split/scan the result unconditionally.
|
|
|
|
Args:
|
|
args: The command and its arguments.
|
|
timeout: Seconds before the command is killed.
|
|
|
|
Returns:
|
|
The command's standard output, or an empty string on any failure.
|
|
"""
|
|
try:
|
|
result = subprocess.run(
|
|
args,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=timeout,
|
|
check=False,
|
|
)
|
|
except subprocess.TimeoutExpired, subprocess.SubprocessError, OSError:
|
|
logger.debug("Command failed: %s", args, exc_info=True)
|
|
return ""
|
|
return result.stdout
|
|
|
|
|
|
def parse_cups_usb_uri(uri: str, info: dict[str, str]) -> None:
|
|
"""Extract the product and serial from a CUPS ``usb://`` URI into ``info``."""
|
|
parsed = urllib.parse.urlparse(uri)
|
|
info["product"] = urllib.parse.unquote(parsed.path.lstrip("/"))
|
|
query = urllib.parse.parse_qs(parsed.query)
|
|
if "serial" in query:
|
|
info["serial"] = query["serial"][0]
|
|
|
|
|
|
def printer_info_from_cups() -> dict[str, str]:
|
|
"""Return the Brother printer's model/serial as parsed from ``lpstat -v``."""
|
|
info: dict[str, str] = {"product": "", "serial": ""}
|
|
for line in run_command_text(["/usr/bin/lpstat", "-v"]).splitlines():
|
|
if "Brother" in line:
|
|
for part in line.split():
|
|
if part.startswith("usb://"):
|
|
parse_cups_usb_uri(part, info)
|
|
break
|
|
return info
|