Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/poetry/packages/locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def get_project_dependencies(
requirement.marker
)

return sorted(nested_dependencies.values(), key=lambda x: x.name.lower())
return nested_dependencies.values()

def get_project_dependency_packages(
self,
Expand Down
24 changes: 16 additions & 8 deletions src/poetry/utils/exporter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import itertools
import urllib.parse

from collections import defaultdict
from typing import TYPE_CHECKING
from typing import Dict
from typing import List
from typing import Optional
from typing import Sequence
from typing import Union
Expand All @@ -15,6 +17,8 @@
from pathlib import Path

from cleo.io.io import IO
from poetry.core.packages.dependency_package import DependencyPackage
from poetry.core.packages.package import Package

from poetry.poetry import Poetry

Expand Down Expand Up @@ -70,14 +74,18 @@ def _export_requirements_txt(
content = ""
dependency_lines = set()

for package, groups in itertools.groupby(
self._poetry.locker.get_project_dependency_packages(
project_requires=self._poetry.package.all_requires,
dev=dev,
extras=extras,
),
lambda dependency_package: dependency_package.package,
# Group by package.
dependency_packages: Dict["Package", List["DependencyPackage"]] = defaultdict(
list
)
for dependency_package in self._poetry.locker.get_project_dependency_packages(
project_requires=self._poetry.package.all_requires,
dev=dev,
extras=extras,
):
dependency_packages[dependency_package.package].append(dependency_package)

for package, groups in dependency_packages.items():
line = ""
dependency_packages = list(groups)
dependency = dependency_packages[0].dependency
Expand Down
108 changes: 108 additions & 0 deletions tests/utils/test_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1775,3 +1775,111 @@ def test_exporter_exports_requirements_txt_to_standard_output(
"""

assert out == expected


def test_exporter_doesnt_confuse_repeated_packages(
tmp_dir: str, poetry: "Poetry", capsys: "CaptureFixture"
):
# Testcase derived from <https://github.com/python-poetry/poetry/issues/5141>.
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "celery",
"version": "5.1.2",
"category": "main",
"optional": False,
"python-versions": "<3.7",
"dependencies": {
"click": ">=7.0,<8.0",
"click-didyoumean": ">=0.0.3",
"click-plugins": ">=1.1.1",
},
},
{
"name": "celery",
"version": "5.2.3",
"category": "main",
"optional": False,
"python-versions": ">=3.7",
"dependencies": {
"click": ">=8.0.3,<9.0",
"click-didyoumean": ">=0.0.3",
"click-plugins": ">=1.1.1",
},
},
{
"name": "click",
"version": "7.1.2",
"category": "main",
"optional": False,
"python-versions": (
">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
),
},
{
"name": "click",
"version": "8.0.3",
"category": "main",
"optional": False,
"python-versions": ">=3.6",
"dependencies": {},
},
{
"name": "click-didyoumean",
"version": "0.0.3",
"category": "main",
"optional": False,
"python-versions": "*",
"dependencies": {"click": "*"},
},
{
"name": "click-didyoumean",
"version": "0.3.0",
"category": "main",
"optional": False,
"python-versions": ">=3.6.2,<4.0.0",
"dependencies": {"click": ">=7"},
},
{
"name": "click-plugins",
"version": "1.1.1",
"category": "main",
"optional": False,
"python-versions": "*",
"dependencies": {"click": ">=4.0"},
},
],
"metadata": {
"lock-version": "1.1",
"python-versions": "^3.6",
"content-hash": (
"832b13a88e5020c27cbcd95faa577bf0dbf054a65c023b45dc9442b640d414e6"
),
"hashes": {
"celery": [],
"click-didyoumean": [],
"click-plugins": [],
"click": [],
},
},
}
)
set_package_requires(poetry)

exporter = Exporter(poetry)

exporter.export("requirements.txt", Path(tmp_dir), sys.stdout)

out, err = capsys.readouterr()
expected = (
'celery==5.1.2 ; python_version < "3.7"\n'
'celery==5.2.3 ; python_version >= "3.7"\n'
"click-didyoumean==0.0.3\n"
'click-didyoumean==0.3.0 ; python_full_version >= "3.6.2" and python_full_version < "4.0.0"\n' # noqa: E501
"click-plugins==1.1.1\n"
'click==7.1.2 ; python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "3.7" or python_full_version >= "3.5.0" and python_version < "3.7" or python_full_version >= "3.6.2" and python_full_version < "4.0.0" or python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"\n' # noqa: E501
'click==8.0.3 ; python_version >= "3.6"\n'
)

assert out == expected