Commit e878a5d4 by Arun Babu Neelicattu

locker: handle cyclic dependencies during walk

Resolves: #3213
parent 89e1d7c1
......@@ -226,45 +226,43 @@ class Locker(object):
next_level_dependencies = []
for requirement in dependencies:
key = (requirement.name, requirement.pretty_constraint)
locked_package = cls.__get_locked_package(requirement, packages_by_name)
if locked_package:
for require in locked_package.requires:
if require.marker.is_empty():
require.marker = requirement.marker
else:
require.marker = require.marker.intersect(requirement.marker)
# create dependency from locked package to retain dependency metadata
# if this is not done, we can end-up with incorrect nested dependencies
marker = requirement.marker
requirement = locked_package.to_dependency()
requirement.marker = requirement.marker.intersect(marker)
key = (requirement.name, requirement.pretty_constraint)
if pinned_versions:
requirement.set_constraint(
locked_package.to_dependency().constraint
)
if key not in nested_dependencies:
for require in locked_package.requires:
if require.marker.is_empty():
require.marker = requirement.marker
else:
require.marker = require.marker.intersect(
requirement.marker
)
require.marker = require.marker.intersect(locked_package.marker)
next_level_dependencies.append(require)
require.marker = require.marker.intersect(locked_package.marker)
next_level_dependencies.append(require)
if requirement.name in project_level_dependencies and level == 0:
# project level dependencies take precedence
continue
if locked_package:
# create dependency from locked package to retain dependency metadata
# if this is not done, we can end-up with incorrect nested dependencies
marker = requirement.marker
requirement = locked_package.to_dependency()
requirement.marker = requirement.marker.intersect(marker)
else:
if not locked_package:
# we make a copy to avoid any side-effects
requirement = deepcopy(requirement)
if pinned_versions:
requirement.set_constraint(
cls.__get_locked_package(requirement, packages_by_name)
.to_dependency()
.constraint
)
# dependencies use extra to indicate that it was activated via parent
# package's extras, this is not required for nested exports as we assume
# the resolver already selected this dependency
requirement.marker = requirement.marker.without_extras()
key = (requirement.name, requirement.pretty_constraint)
if key not in nested_dependencies:
nested_dependencies[key] = requirement
else:
......
......@@ -696,6 +696,62 @@ foo @ git+https://github.com/foo/foo.git@123456
assert expected == content
def test_exporter_can_export_requirements_txt_with_nested_packages_cyclic(
tmp_dir, poetry
):
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "foo",
"version": "1.2.3",
"category": "main",
"optional": False,
"python-versions": "*",
"dependencies": {"bar": {"version": "4.5.6"}},
},
{
"name": "bar",
"version": "4.5.6",
"category": "main",
"optional": False,
"python-versions": "*",
"dependencies": {"baz": {"version": "7.8.9"}},
},
{
"name": "baz",
"version": "7.8.9",
"category": "main",
"optional": False,
"python-versions": "*",
"dependencies": {"foo": {"version": "1.2.3"}},
},
],
"metadata": {
"python-versions": "*",
"content-hash": "123456789",
"hashes": {"foo": [], "bar": [], "baz": []},
},
}
)
set_package_requires(poetry, skip={"bar", "baz"})
exporter = Exporter(poetry)
exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt")
with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f:
content = f.read()
expected = """\
bar==4.5.6
baz==7.8.9
foo==1.2.3
"""
assert expected == content
def test_exporter_can_export_requirements_txt_with_git_packages_and_markers(
tmp_dir, poetry
):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment