Files
ComfyAssets-ComfyUI_PromptM…/tests/test_packaging.py
T
Vito cfe47d3cb0 ci: support and test Python 3.12-3.14 only (#356) (#357)
* ci: test Python 3.11-3.14 and require Python 3.11 (#356)

Python 3.10 reaches end of life in October 2026 and no PromptManager
users run it; 3.14 is current and was untested. The test matrix runs
3.11-3.14 on Linux, Windows and macOS, bandit runs on 3.11, and
requires-python is >=3.11 so the package no longer claims a version CI
does not test. The packaging test drops its 3.10-only tomllib fallback.
The required checks follow the matrix (py3.10 -> py3.14).

* ci: narrow Python support to 3.12-3.14 (#356)

ComfyUI recommends 3.13, names 3.12 as the fallback and 3.14 as working
(its README); PromptManager supports exactly those. requires-python is
>=3.12, the test matrix and the required checks drop 3.11, and bandit
runs on 3.12.
2026-10-26 22:23:06 -07:00

83 lines
2.6 KiB
Python

"""Packaging and repository hygiene contracts.
These guard the metadata that ComfyUI-Manager, the Comfy Registry and
``pip install .`` consume, and the ignore rules that keep local secrets
(``config.json`` holds the CivitAI API key) out of the repository.
"""
import os
import re
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# PEP 508 requirement: name, optional extras, optional version spec.
_VERSION_CLAUSE = r"[<>=!~]=?\s*[A-Za-z0-9.*+!-]+\s*"
_REQUIREMENT = re.compile(
r"^[A-Za-z0-9][A-Za-z0-9._-]*" # distribution name
r"(\[[A-Za-z0-9,._-]+\])?\s*" # optional extras
r"(" + _VERSION_CLAUSE + r"(,\s*" + _VERSION_CLAUSE + r")*)?$" # version spec
)
def _read(name):
with open(os.path.join(ROOT, name), encoding="utf-8") as fh:
return fh.read()
def _pyproject():
import tomllib
return tomllib.loads(_read("pyproject.toml"))
class PyprojectContract(unittest.TestCase):
def test_dependencies_are_valid_requirements(self):
deps = _pyproject()["project"]["dependencies"]
self.assertTrue(deps, "dependencies must not be empty")
for dep in deps:
self.assertRegex(dep, _REQUIREMENT, f"not a PEP 508 requirement: {dep!r}")
def test_dependencies_match_requirements_txt(self):
deps = set(_pyproject()["project"]["dependencies"])
wanted = {
line.split("#", 1)[0].strip()
for line in _read("requirements.txt").splitlines()
if line.split("#", 1)[0].strip()
}
self.assertEqual(deps, wanted)
def test_requires_python_floor_is_3_12(self):
self.assertEqual(_pyproject()["project"]["requires-python"], ">=3.12")
def test_build_system_is_declared(self):
self.assertIsNotNone(_pyproject().get("build-system"))
class IgnoreRulesContract(unittest.TestCase):
def test_local_secrets_and_artifacts_are_ignored(self):
patterns = {
line.strip()
for line in _read(".gitignore").splitlines()
if line.strip() and not line.startswith("#")
}
for required in (
"config.json",
"resume.sh",
"*.backup_*",
"promptmanager_logs/",
):
self.assertIn(required, patterns)
class LintConfigContract(unittest.TestCase):
def test_flake8_reads_its_own_config_file(self):
# flake8 ignores pyproject.toml, so the settings must live in .flake8.
text = _read(".flake8")
self.assertIn("max-line-length = 88", text)
self.assertIn("E203", text)
if __name__ == "__main__":
unittest.main()