Skip to content

Commit 44a89cb

Browse files
dimblebyradoering
andauthored
canonicalized extra names (python-poetry#6541)
Co-authored-by: Randy Döring <[email protected]>
1 parent 85a0913 commit 44a89cb

File tree

16 files changed

+65
-62
lines changed

16 files changed

+65
-62
lines changed

poetry.lock

Lines changed: 13 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ generate-setup-file = false
4545
python = "^3.7"
4646

4747
poetry-core = "^1.2.0"
48-
poetry-plugin-export = "^1.0.7"
48+
poetry-plugin-export = "^1.1.1"
4949
"backports.cached-property" = { version = "^1.0.2", python = "<3.8" }
5050
cachecontrol = { version = "^0.12.9", extras = ["filecache"] }
5151
cachy = "^0.3.0"
@@ -77,6 +77,8 @@ urllib3 = "^1.26.0"
7777
pre-commit = "^2.6"
7878

7979
[tool.poetry.group.test.dependencies]
80+
# TODO: remove as soon as poetry-core with poetry-core#476 is available
81+
poetry-core = { git = "https://github.com/dimbleby/poetry-core.git", branch = "canonicalize-extras" }
8082
deepdiff = "^5.0"
8183
flatdict = "^4.0.1"
8284
httpretty = "^1.0"

src/poetry/console/commands/install.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ def handle(self) -> int:
115115
)
116116
return 1
117117

118+
extras: list[str]
118119
if self.option("all-extras"):
119120
extras = list(self.poetry.package.extras.keys())
120121
else:

src/poetry/installation/installer.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def __init__(
6363

6464
self._whitelist: list[NormalizedName] = []
6565

66-
self._extras: list[str] = []
66+
self._extras: list[NormalizedName] = []
6767

6868
if executor is None:
6969
executor = Executor(
@@ -175,7 +175,7 @@ def whitelist(self, packages: Iterable[str]) -> Installer:
175175
return self
176176

177177
def extras(self, extras: list[str]) -> Installer:
178-
self._extras = extras
178+
self._extras = [canonicalize_name(extra) for extra in extras]
179179

180180
return self
181181

@@ -259,8 +259,12 @@ def _do_install(self) -> int:
259259
"</warning>"
260260
)
261261

262+
locker_extras = {
263+
canonicalize_name(extra)
264+
for extra in self._locker.lock_data.get("extras", {})
265+
}
262266
for extra in self._extras:
263-
if extra not in self._locker.lock_data.get("extras", {}):
267+
if extra not in locker_extras:
264268
raise ValueError(f"Extra [{extra}] is not specified.")
265269

266270
# If we are installing from lock
@@ -538,11 +542,17 @@ def _get_extra_packages(self, repo: Repository) -> set[NormalizedName]:
538542
539543
Maybe we just let the solver handle it?
540544
"""
541-
extras: dict[str, list[str]]
545+
extras: dict[NormalizedName, list[NormalizedName]]
542546
if self._update:
543547
extras = {k: [d.name for d in v] for k, v in self._package.extras.items()}
544548
else:
545-
extras = self._locker.lock_data.get("extras", {})
549+
raw_extras = self._locker.lock_data.get("extras", {})
550+
extras = {
551+
canonicalize_name(extra): [
552+
canonicalize_name(dependency) for dependency in dependencies
553+
]
554+
for extra, dependencies in raw_extras.items()
555+
}
546556

547557
return get_extra_package_names(repo.packages, extras, self._extras)
548558

src/poetry/packages/locker.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from typing import Any
1212
from typing import cast
1313

14+
from packaging.utils import canonicalize_name
1415
from poetry.core.packages.dependency import Dependency
1516
from poetry.core.packages.package import Package
1617
from poetry.core.semver.helpers import parse_constraint
@@ -164,6 +165,7 @@ def locked_repository(self) -> LockfileRepository:
164165
extras = info.get("extras", {})
165166
if extras:
166167
for name, deps in extras.items():
168+
name = canonicalize_name(name)
167169
package.extras[name] = []
168170

169171
for dep in deps:

src/poetry/puzzle/provider.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
from poetry.puzzle.exceptions import OverrideNeeded
3434
from poetry.repositories.exceptions import PackageNotFound
3535
from poetry.utils.helpers import download_file
36-
from poetry.utils.helpers import safe_extra
3736
from poetry.vcs.git import Git
3837

3938

@@ -580,7 +579,6 @@ def complete_package(
580579
# to the current package
581580
if dependency.extras:
582581
for extra in dependency.extras:
583-
extra = safe_extra(extra)
584582
if extra not in package.extras:
585583
continue
586584

@@ -615,9 +613,7 @@ def complete_package(
615613
(dep.is_optional() and dep.name not in optional_dependencies)
616614
or (
617615
dep.in_extras
618-
and not set(dep.in_extras).intersection(
619-
{safe_extra(extra) for extra in dependency.extras}
620-
)
616+
and not set(dep.in_extras).intersection(dependency.extras)
621617
)
622618
):
623619
continue

src/poetry/utils/extras.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
def get_extra_package_names(
1616
packages: Iterable[Package],
17-
extras: Mapping[str, list[str]],
18-
extra_names: Collection[str],
17+
extras: Mapping[NormalizedName, Iterable[NormalizedName]],
18+
extra_names: Collection[NormalizedName],
1919
) -> set[NormalizedName]:
2020
"""
2121
Returns all package names required by the given extras.

src/poetry/utils/helpers.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import os
4-
import re
54
import shutil
65
import stat
76
import sys
@@ -171,18 +170,6 @@ def pluralize(count: int, word: str = "") -> str:
171170
return word + "s"
172171

173172

174-
def safe_extra(extra: str) -> str:
175-
"""Convert an arbitrary string to a standard 'extra' name.
176-
177-
Any runs of non-alphanumeric characters are replaced with a single '_',
178-
and the result is always lowercased.
179-
180-
See
181-
https://github.com/pypa/setuptools/blob/452e13c/pkg_resources/__init__.py#L1423-L1431.
182-
"""
183-
return re.sub("[^A-Za-z0-9.-]+", "_", extra).lower()
184-
185-
186173
def _get_win_folder_from_registry(csidl_name: str) -> str:
187174
if sys.platform != "win32":
188175
raise RuntimeError("Method can only be called on Windows.")

tests/console/commands/test_install.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,10 @@ def test_all_extras_populates_installer(tester: CommandTester, mocker: MockerFix
166166

167167
tester.execute("--all-extras")
168168

169-
assert tester.command.installer._extras == ["extras_a", "extras_b"]
169+
assert tester.command.installer._extras == ["extras-a", "extras-b"]
170170

171171

172-
def test_extras_conlicts_all_extras(tester: CommandTester, mocker: MockerFixture):
172+
def test_extras_conflicts_all_extras(tester: CommandTester, mocker: MockerFixture):
173173
"""
174174
The --extras doesn't make sense with --all-extras.
175175
"""

tests/installation/fixtures/with-dependencies-nested-extras.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ python-versions = "*"
88
files = []
99

1010
[package.dependencies]
11-
B = {version = "^1.0", optional = true, extras = ["C"]}
11+
B = {version = "^1.0", optional = true, extras = ["c"]}
1212

1313
[package.extras]
14-
b = ["B[C] (>=1.0,<2.0)"]
14+
b = ["B[c] (>=1.0,<2.0)"]
1515

1616
[[package]]
1717
name = "B"

0 commit comments

Comments
 (0)