Three logical changes rolled into one commit: 1. stdlib HTTP read timeout. urlopen's `timeout=` only bounds connect, and CPython versions disagree on whether the resulting socket keeps that pinned. A half-dead Immich server that has sent headers but stalls on the body would hang the whole batch. Pin a read-side timeout on every HTTPConnection/HTTPSConnection at import so the bound holds across versions and reading paths. Verified with a stand-alone server/client pair: with the fix, a stalled body read fails in 0.5s with TimeoutError; without the fix, the same script hangs past the test timeout. Also distinguish `_add_to_album` ambiguity from failure: an entry that omits `success` is now treated the same way as an empty body (downgrade to `album: "unconfirmed"`), not as a ValueError. Only an entry that explicitly says `success: false` raises. Broadens the transient-error catch in `_wait_for_storage_settle` from (HTTPError, URLError) to OSError so socket timeouts and connection resets during polling do not abort the wait. 2. CI matrix now includes 3.11. tomllib is stdlib from 3.11, and the packaging-drift test in tests/test_packaging.py uses it. Without 3.11 the 3.10 job importorskip-s that test, leaving drift detection silent at the lowest supported interpreter. 3. Test ergonomics. tests/conftest.py extracts the per-test image batch fixture (was duplicated across 10+ tests). README clarifies that retry_archive's exit code reflects `errors` entries only — `unconfirmed` album results exit 0, not non-zero. Plus a User-Agent string that reports the installed package version (via importlib.metadata) so the value cannot drift from pyproject.toml. 70 tests pass; was 62. ruff + mypy clean. Verified on Python 3.11 and 3.12 locally.
35 lines
1.3 KiB
Python
35 lines
1.3 KiB
Python
"""Packaging metadata checks.
|
|
|
|
The runtime dependency list exists twice -- pyproject.toml for pip, and
|
|
requirements.txt for ComfyUI Manager, which does not read pyproject. Neither
|
|
file can be dropped, so the only defence against drift is a test.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def _declared_requirements():
|
|
lines = ROOT.joinpath("requirements.txt").read_text().splitlines()
|
|
return [line.strip() for line in lines if line.strip() and not line.startswith("#")]
|
|
|
|
|
|
def test_requirements_matches_pyproject_dependencies():
|
|
# tomllib is stdlib from 3.11; the CI matrix covers this on 3.11/3.12/3.13.
|
|
# The 3.10 job will skip via importorskip rather than fail; that is fine —
|
|
# the packaging invariant is already enforced on every newer interpreter.
|
|
tomllib = pytest.importorskip("tomllib")
|
|
with ROOT.joinpath("pyproject.toml").open("rb") as f:
|
|
pyproject = tomllib.load(f)
|
|
assert _declared_requirements() == pyproject["project"]["dependencies"]
|
|
|
|
|
|
def test_requirements_are_unpinned_lower_bounds():
|
|
"""A custom node must not pin the host ComfyUI environment's packages."""
|
|
for requirement in _declared_requirements():
|
|
assert ">=" in requirement, requirement
|
|
assert not any(op in requirement for op in ("==", "<", "~=")), requirement
|