Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing vias when more than two input files are used #1890

Merged
merged 10 commits into from
Jul 1, 2023
26 changes: 21 additions & 5 deletions piptools/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,18 @@ def __init__(
self.unsafe_constraints: set[InstallRequirement] = set()

self.existing_constraints = existing_constraints
self._constraints_map = {key_from_ireq(ireq): ireq for ireq in constraints}

# Categorize InstallRequirements into sets by key
constraints_sets: DefaultDict[
str, set[InstallRequirement]
] = collections.defaultdict(set)
for ireq in constraints:
constraints_sets[key_from_ireq(ireq)].add(ireq)
# Collapse each set of InstallRequirements using combine_install_requirements
self._constraints_map = {
ireq_key: combine_install_requirements(ireqs)
for ireq_key, ireqs in constraints_sets.items()
}

# Make sure there is no enabled legacy resolver
options.deprecated_features_enabled = omit_list_value(
Expand Down Expand Up @@ -759,9 +770,14 @@ def _get_install_requirement_from_candidate(
ireq_key = key_from_ireq(ireq)
pinned_ireq._required_by = reverse_dependencies.get(ireq_key, set())

# Save source for annotation
source_ireq = self._constraints_map.get(ireq_key)
if source_ireq is not None:
pinned_ireq._source_ireqs = [source_ireq]
# Save sources for annotation
constraint_ireq = self._constraints_map.get(ireq_key)
if constraint_ireq is not None:
if hasattr(constraint_ireq, "_source_ireqs"):
# If the constraint is combined (has _source_ireqs), use those
pinned_ireq._source_ireqs = constraint_ireq._source_ireqs
else:
# Otherwise (the constraint is not combined) it is the source
pinned_ireq._source_ireqs = [constraint_ireq]

return pinned_ireq
36 changes: 36 additions & 0 deletions tests/test_cli_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,42 @@ def test_upgrade_package_doesnt_remove_annotation(pip_conf, runner):
)


@pytest.mark.parametrize(("num_inputs"), (2, 3, 10))
def test_many_inputs_includes_all_annotations(pip_conf, runner, tmp_path, num_inputs):
"""
Tests that an entry required by multiple input files is attributed to all of them in the
annotation.
See: https://github.com/jazzband/pip-tools/issues/1853
"""
req_ins = [tmp_path / f"requirements{n:02d}.in" for n in range(num_inputs)]
for req_in in req_ins:
req_in.write_text("small-fake-a==0.1\n")

out = runner.invoke(
cli,
[
"--output-file",
"-",
"--quiet",
"--no-header",
"--no-emit-find-links",
]
+ [str(r) for r in req_ins],
)
assert out.exit_code == 0, out.stderr
assert (
out.stdout
== "\n".join(
[
"small-fake-a==0.1",
" # via",
]
+ [f" # -r {req_in}" for req_in in req_ins]
)
+ "\n"
)


@pytest.mark.parametrize(
"options",
(
Expand Down