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:
parent
17246a0c8f
commit
d2e098ac21
@ -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"
|
||||
|
||||
@ -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 ----
|
||||
|
||||
Loading…
Reference in New Issue
Block a user