%pyproject_patch_dependency: Validate arguments early, in %prep

When python3 and pyproject_dependency_overrides.py are available,
the macro now calls parse_override_string at %prep time,
catching invalid overrides immediately instead of in %pyproject_buildrequires.
When only pyproject-srpm-macros is installed, validation is skipped gracefully.

Assisted-By: Claude Opus 4.6
This commit is contained in:
Miro Hrončok 2026-04-28 23:01:21 +02:00
parent 965cf2fb3b
commit ef1bdc962d
4 changed files with 67 additions and 0 deletions

View File

@ -20,6 +20,9 @@
%pyproject_patch_dependency() %{expand:\\\
%{!?1:%{error:%%pyproject_patch_dependency requires an argument}}\\\
%{?2:%{error:%%pyproject_patch_dependency accepts exactly one argument per call}}\\\
if [ -f %{__python3} ] && [ -f %{_rpmconfigdir}/redhat/pyproject_dependency_overrides.py ]; then
%{__python3} -Bs %{_rpmconfigdir}/redhat/pyproject_dependency_overrides.py '%1'
fi
echo '%1' >> %{_pyproject_dep_overrides}
}

View File

@ -183,6 +183,7 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856
- Add long option support for all public parametric macros
- E.g. %%pyproject_buildrequires --no-runtime is equivalent to %%pyproject_buildrequires -R
- %%pyproject_save_files: Allow to use --auto instead of +auto
- %%pyproject_patch_dependency: Validate arguments early, in %%prep
* Wed Apr 29 2026 Tomáš Hrnčiar <thrnciar@redhat.com> - 1.21.0-1
- Implement extras validation

View File

@ -135,3 +135,13 @@ def apply_overrides_to_specifiers(specifiers, overrides, package_name=None,
new_specifiers.append(Specifier(f'>={value}'))
return new_specifiers
if __name__ == '__main__':
import sys
override_str = sys.argv[1].removesuffix(':br_only')
try:
parse_override_string(override_str)
except ValueError as e:
print(e, file=sys.stderr)
sys.exit(1)

View File

@ -1,5 +1,8 @@
"""Unit tests for dependency override functionality."""
import subprocess
import sys
import pytest
from packaging.requirements import Requirement
@ -47,6 +50,56 @@ class TestParseOverrideString:
parse_override_string('pkg:set_upper:not_a_version!!!')
# ---- CLI validation (pyproject_dependency_overrides.py __main__) ----
SCRIPT = 'pyproject_dependency_overrides.py'
class TestCLIValidation:
def _run(self, arg):
return subprocess.run(
[sys.executable, '-Bs', SCRIPT, arg],
capture_output=True, text=True,
)
def test_valid_drop_upper(self):
assert self._run('numpy:drop_upper').returncode == 0
def test_valid_set_upper(self):
assert self._run('numpy:set_upper:2.0').returncode == 0
def test_valid_ignore_br_only(self):
assert self._run('pkg:ignore:br_only').returncode == 0
def test_valid_set_lower_br_only(self):
assert self._run('pkg:set_lower:1.0:br_only').returncode == 0
def test_invalid_no_colon(self):
r = self._run('justpackage')
assert r.returncode == 1
assert 'Invalid dependency override format' in r.stderr
def test_invalid_action(self):
r = self._run('pkg:bogus')
assert r.returncode == 1
assert 'Invalid dependency override action' in r.stderr
def test_set_upper_missing_value(self):
r = self._run('pkg:set_upper')
assert r.returncode == 1
assert 'requires a value' in r.stderr
def test_drop_upper_rejects_value(self):
r = self._run('pkg:drop_upper:2.0')
assert r.returncode == 1
assert 'does not accept a value' in r.stderr
def test_invalid_version(self):
r = self._run('pkg:set_upper:not_a_version!!!')
assert r.returncode == 1
assert 'Invalid version' in r.stderr
# ---- apply_overrides_to_specifiers (shared module) ----
class TestApplyOverridesToSpecifiers: