Refactor _apply_dependency_overrides to accept and return a Requirement object

The override check in self.dependency_overrides was done twice: once at
the call site and once inside _apply_dependency_overrides. By moving the
full logic into the method and having it accept a Requirement directly,
the redundant check is eliminated.

Also remove _base_package_name, since Requirement.name already strips
extras and the only remaining need is canonicalize_name.

Assisted-By: Claude Opus 4.6
(cherry picked from Fedora commit 9b2f271530)
This commit is contained in:
Miro Hrončok 2026-05-13 12:27:55 +02:00
parent 17246a0c8f
commit d2e098ac21
2 changed files with 56 additions and 90 deletions

View File

@ -139,31 +139,25 @@ class Requirements:
return parsed_overrides
@staticmethod
def _base_package_name(package_name):
"""Extract base package name, stripping any extras like [extra1]."""
bracket = package_name.find('[')
if bracket != -1:
package_name = package_name[:bracket]
return canonicalize_name(package_name)
def _should_ignore_dependency(self, package_name):
"""Check if a dependency should be completely ignored."""
package_name = self._base_package_name(package_name)
if package_name not in self.dependency_overrides:
return False
return any(o['action'] == 'ignore' for o in self.dependency_overrides[package_name])
def _apply_dependency_overrides(self, package_name, specifiers):
def _apply_dependency_overrides(self, requirement):
"""Apply dependency overrides to a list of specifiers for a given package."""
package_name = self._base_package_name(package_name)
package_name = canonicalize_name(requirement.name)
if package_name not in self.dependency_overrides:
return specifiers
return requirement
return apply_overrides_to_specifiers(
specifiers, self.dependency_overrides[package_name],
overridden = apply_overrides_to_specifiers(
requirement.specifier, self.dependency_overrides[package_name],
package_name=package_name, log_fn=print_err)
requirement.specifier = SpecifierSet(','.join(str(s) for s in overridden))
return requirement
@property
def marker_envs(self):
if self.extras:
@ -242,11 +236,7 @@ class Requirements:
# Apply dependency overrides before the installed-version check,
# so the check reflects the constraints we will actually output.
if self._base_package_name(name) in self.dependency_overrides:
overridden = self._apply_dependency_overrides(
name, list(requirement.specifier))
requirement.specifier = SpecifierSet(
','.join(str(s) for s in overridden))
requirement = self._apply_dependency_overrides(requirement)
# We need to always accept pre-releases as satisfying the requirement
# Otherwise e.g. installed cffi version 1.15.0rc2 won't even satisfy the requirement for "cffi"

View File

@ -330,22 +330,6 @@ class TestParseDependencyOverrides:
self._parse(['pkg:ignore:somevalue'])
# ---- Requirements._base_package_name ----
class TestBasePackageName:
def test_simple_name(self):
assert Requirements._base_package_name('requests') == 'requests'
def test_with_extras(self):
assert Requirements._base_package_name('requests[security]') == 'requests'
def test_normalization(self):
assert Requirements._base_package_name('My_Package') == 'my-package'
def test_with_extras_and_normalization(self):
assert Requirements._base_package_name('My_Package[extra]') == 'my-package'
# ---- Requirements._should_ignore_dependency ----
class TestShouldIgnoreDependency:
@ -375,81 +359,75 @@ class TestApplyDependencyOverrides:
raise Exception('not installed')
return Requirements(mock_version, dependency_overrides=overrides)
def _specs(self, spec_str):
return list(SpecifierSet(spec_str))
def test_no_overrides(self):
r = self._make_req([])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert len(result) == 2
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
assert len(list(result.specifier)) == 2
def test_drop_upper(self):
r = self._make_req(['numpy:drop_upper'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert len(result) == 1
assert result[0].operator == '>='
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
specs = list(result.specifier)
assert len(specs) == 1
assert specs[0].operator == '>='
def test_drop_lower(self):
r = self._make_req(['numpy:drop_lower'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert len(result) == 1
assert result[0].operator == '<'
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
specs = list(result.specifier)
assert len(specs) == 1
assert specs[0].operator == '<'
def test_drop_constraints(self):
r = self._make_req(['numpy:drop_constraints'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert result == []
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
assert list(result.specifier) == []
def test_set_upper(self):
r = self._make_req(['numpy:set_upper:3.0'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert len(result) == 2
ops = {s.operator for s in result}
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
specs = list(result.specifier)
assert len(specs) == 2
ops = {s.operator for s in specs}
assert '>=' in ops
assert '<' in ops
upper = [s for s in result if s.operator == '<'][0]
upper = [s for s in specs if s.operator == '<'][0]
assert upper.version == '3.0'
def test_set_lower(self):
r = self._make_req(['numpy:set_lower:0.5'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert len(result) == 2
ops = {s.operator for s in result}
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
specs = list(result.specifier)
assert len(specs) == 2
ops = {s.operator for s in specs}
assert '>=' in ops
assert '<' in ops
lower = [s for s in result if s.operator == '>='][0]
lower = [s for s in specs if s.operator == '>='][0]
assert lower.version == '0.5'
def test_drop_upper_decomposes_pin(self):
"""drop_upper on == decomposes to >= (keeps lower half)."""
r = self._make_req(['attrs:drop_upper'])
specs = self._specs('==25.3.0')
result = r._apply_dependency_overrides('attrs', specs)
assert len(result) == 1
assert result[0].operator == '>='
assert result[0].version == '25.3.0'
result = r._apply_dependency_overrides(Requirement('attrs==25.3.0'))
specs = list(result.specifier)
assert len(specs) == 1
assert specs[0].operator == '>='
assert specs[0].version == '25.3.0'
def test_drop_lower_decomposes_pin(self):
"""drop_lower on == decomposes to <= (keeps upper half)."""
r = self._make_req(['attrs:drop_lower'])
specs = self._specs('==25.3.0')
result = r._apply_dependency_overrides('attrs', specs)
assert len(result) == 1
assert result[0].operator == '<='
assert result[0].version == '25.3.0'
result = r._apply_dependency_overrides(Requirement('attrs==25.3.0'))
specs = list(result.specifier)
assert len(specs) == 1
assert specs[0].operator == '<='
assert specs[0].version == '25.3.0'
def test_drop_upper_preserves_exclusions(self):
"""!= exclusions are always preserved."""
r = self._make_req(['pkg:drop_upper'])
specs = self._specs('>=1.0,!=1.5,<3.0')
result = r._apply_dependency_overrides('pkg', specs)
ops = [s.operator for s in result]
result = r._apply_dependency_overrides(Requirement('pkg>=1.0,!=1.5,<3.0'))
ops = [s.operator for s in result.specifier]
assert '>=' in ops
assert '!=' in ops
assert '<' not in ops
@ -457,31 +435,29 @@ class TestApplyDependencyOverrides:
def test_ignore_action_passthrough(self):
"""ignore action is handled separately; _apply_dependency_overrides skips it."""
r = self._make_req(['pkg:ignore'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('pkg', specs)
assert len(result) == 2
result = r._apply_dependency_overrides(Requirement('pkg>=1.0,<2.0'))
assert len(list(result.specifier)) == 2
def test_unrelated_package_not_affected(self):
r = self._make_req(['other:drop_upper'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy', specs)
assert len(result) == 2
result = r._apply_dependency_overrides(Requirement('numpy>=1.0,<2.0'))
assert len(list(result.specifier)) == 2
def test_extras_stripped_for_lookup(self):
r = self._make_req(['numpy:drop_upper'])
specs = self._specs('>=1.0,<2.0')
result = r._apply_dependency_overrides('numpy[extra1]', specs)
assert len(result) == 1
assert result[0].operator == '>='
result = r._apply_dependency_overrides(Requirement('numpy[extra1]>=1.0,<2.0'))
specs = list(result.specifier)
assert len(specs) == 1
assert specs[0].operator == '>='
def test_drop_upper_decomposes_tilde(self):
"""drop_upper on ~= decomposes to >= (PEP 440: ~=V.N is >=V.N, ==V.*)."""
r = self._make_req(['pkg:drop_upper'])
specs = self._specs('~=1.4')
result = r._apply_dependency_overrides('pkg', specs)
assert len(result) == 1
assert result[0].operator == '>='
assert result[0].version == '1.4'
result = r._apply_dependency_overrides(Requirement('pkg~=1.4'))
specs = list(result.specifier)
assert len(specs) == 1
assert specs[0].operator == '>='
assert specs[0].version == '1.4'
# ---- pyproject_patch_metadata ----