ADR-1236: Single-source package versions and unify Python dependencies¶
- Status: Accepted
- Date: 2026-09-08
- Deciders: Lusoris, Claude (Anthropic)
- Tags: build, ci, python, packaging, renovate
Context¶
Prior to this decision, package versions and dependencies across the repository suffered from two forms of fragmentation:
- Python Dependency Triplication: The runtime dependency list for the
vmafpackage was declared in three separate places: python/pyproject.tomlunder[project].dependenciespython/requirements.txtpython/setup.pyunderinstall_requires=[...]
Renovate evaluates distinct package managers independently (pep621 for pyproject.toml, pip_requirements for requirements.txt, and pip_setup for setup.py). As a result, every single dependency update opened two competing, unmergeable PRs (such as #1409 touching requirements.txt and #1410 touching pyproject.toml and setup.py).
- Cross-Tree Version Disagreements: A repository-wide audit revealed 25 packages declared in more than one file, with 5 instances of version skew:
numpy: 10 sites with disparate versions (1.26.0,2.0.0,2.3.3,2.5.2, and unpinned in[build-system]).scipy: 8 sites drifting between1.14.1and1.18.1.matplotlib: 4 sites drifting between3.9.3and3.11.1.pyarrow: 3 sites drifting between13.0.0and25.0.1.- Package version manifests:
tools/vmaf-tune/pyproject.tomldeclared0.0.1whilesrc/vmaftune/__init__.pydeclared0.0.2; andbuild-config.envlacked an authoritativeVMAFX_VERSIONknob.
Decision¶
python/pyproject.toml [project].dependenciesis the single owner of python package dependencies:python/setup.pyremoves the duplicateinstall_requires=[...]definition. Modern setuptools natively loads[project].dependenciesfrompyproject.toml.python/requirements.txtis derived mechanically frompython/pyproject.tomlviascripts/ci/check-python-requirements-single-source.sh --write(wrapped asmake python-deps-sync).scripts/ci/check-python-requirements-single-source.shruns as a CI gate and pre-commit hook to compare the ordered dependency entries (comments and blank lines are ignored).-
renovate.jsonaddspython/requirements.txttoignorePaths. Renovate updates onlypython/pyproject.toml, eliminating duplicate and competing PRs. -
Resolution of Version Disagreements (Newest-Version Rule): Per explicit policy, all divergent pins are resolved to the newest version rather than the lowest common denominator:
- The
vmafNumPy build floor follows its current runtime floor,>=2.5.3; rebasing preserves the already merged NumPy and PyWavelets updates. scipyis unified to>=1.18.1.matplotlibis unified to>=3.11.1.pyarrowis unified to>=25.0.1.tools/vmaf-tune/pyproject.tomlis bumped to0.0.2to match its implementation.optunaintools/vmaf-tune/pyproject.toml [dev]is aligned to>=5.0.0.-
mcp-server/vmaf-mcp/pyproject.tomlreplaceshatchling==1.32.0withhatchling>=1.32.0. -
Extend
build-config.envas the global version single-source:build-config.envis augmented with: VMAFX_VERSION="3.2.1"(tracked inrelease-please-config.jsonextra-files) Scientific Python floors remain owned by each package manifest. The five initially proposed scientific-stack globals had no consumers or drift checks and were removed during review. Native ORT archive roles remain separate from Python dependency floors; see the ownership guide.scripts/ci/check-workflow-versions.pyis updated to validatePYTHON_CI_VERSIONandVMAFX_VERSIONagainst the workflows and core manifests.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
pyproject.toml single owner + generated requirements.txt + gate (chosen) | Eliminates Renovate duplicates; retains compatibility with pip -r workflows and Docker builds; prevents silent drift | requirements.txt is checked into git | Standard repository pattern (matches build-config.env / base-images-sync) |
Delete requirements.txt completely | Zero duplication | Breaks upstream Netflix documentation, Dockerfile recipes, and legacy pip install -r consumers | Unnecessary breakage when mechanical derivation solves the drift |
Keep requirements.txt as primary and generate pyproject.toml | Legacy familiarity | Violates PEP 621 standards; setuptools and modern build backends expect pyproject.toml | pyproject.toml is the standard Python packaging metadata authority |
| Lowest-common-denominator version alignment | Minimizes risk of upstream compatibility issues | Stagnates dependency versions; runs counter to explicit user requirement for current versions | User explicitly mandated newest version resolution |
Consequences¶
- Positive:
- Renovate no longer manages the generated
vmafrequirements list separately from its package metadata. - The ordered requirements list is checked against package metadata locally and in CI;
setup.pyno longer duplicates it. - The changed package metadata and workflow mirrors have explicit owners; this is not a claim that every historical or compatibility pin has been unified.
- Negative:
- Developers editing
python/pyproject.tomldependencies must runmake python-deps-syncbefore committing (enforced by pre-commit hook). - Neutral / follow-ups:
release-pleaseautomatically updatesbuild-config.envon version cuts.
References¶
- Follows the authority-plus-drift-check model of ADR-1231.
- Python package metadata standards: PEP 517, PEP 518, PEP 621.
-
Refs user prompt requirement on branch
build/version-single-source-tree. -
req(2026-09-08): “everything that needs to be configured in mutliple places is in global envs”. A declaration becomes an owner only when consumers or executable drift checks use it.