Skip to content

Commit a17f1ba

Browse files
Added canonicalize_name() to console/commands/init.py (#5076)
* Added canonicalize_name() to console/commands/init.py * Removed unnecessary helper function calls * Fixed capitalized package bug on poetry init Used canonicalize_name to standardize the names of all packages in poetry init with interactive dependencies. Given its importance, added a test_canoncalize_name() test to tests/utils/test_helpers.py * Split out _get_choice_list to a separate function For testing purposes * Tests working with canonicalized_name assigned outside of _generate_choice_list() * Changed test_generate_choice_list Parameterized a _generate_choice_list_packages fixture in order to call the test with both > 10 and < 10 cases, verifying that output is <= 10 and the package_name has the desired package first in choices * Added output message if choice_list is truncated Also added cases for truncation and non-truncation in test_generate_choice_list to ensure the message is printed in only the correct cases * Removed info_string calls Moved info_string generation logic out of init._generate_choice_list * Removed type hint for info_string
1 parent c1c74b9 commit a17f1ba

File tree

3 files changed

+105
-20
lines changed

3 files changed

+105
-20
lines changed

src/poetry/console/commands/init.py

+32-20
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
from poetry.console.commands.command import Command
2020
from poetry.console.commands.env_command import EnvCommand
21+
from poetry.utils.helpers import canonicalize_name
2122

2223

2324
if TYPE_CHECKING:
25+
from poetry.core.packages.package import Package
2426
from tomlkit.items import InlineTable
2527

2628
from poetry.repositories import Pool
@@ -242,6 +244,26 @@ def handle(self) -> int:
242244

243245
return 0
244246

247+
def _generate_choice_list(
248+
self, matches: List["Package"], canonicalized_name: str
249+
) -> List[str]:
250+
choices = []
251+
matches_names = [p.name for p in matches]
252+
exact_match = canonicalized_name in matches_names
253+
if exact_match:
254+
choices.append(matches[matches_names.index(canonicalized_name)].pretty_name)
255+
256+
for found_package in matches:
257+
if len(choices) >= 10:
258+
break
259+
260+
if found_package.name == canonicalized_name:
261+
continue
262+
263+
choices.append(found_package.pretty_name)
264+
265+
return choices
266+
245267
def _determine_requirements(
246268
self,
247269
requires: List[str],
@@ -254,7 +276,7 @@ def _determine_requirements(
254276
package = self.ask(
255277
"Search for package to add (or leave blank to continue):"
256278
)
257-
while package is not None:
279+
while package:
258280
constraint = self._parse_requirements([package])[0]
259281
if (
260282
"git" in constraint
@@ -267,34 +289,24 @@ def _determine_requirements(
267289
package = self.ask("\nAdd a package:")
268290
continue
269291

270-
matches = self._get_pool().search(constraint["name"])
271-
292+
canonicalized_name = canonicalize_name(constraint["name"])
293+
matches = self._get_pool().search(canonicalized_name)
272294
if not matches:
273295
self.line("<error>Unable to find package</error>")
274296
package = False
275297
else:
276-
choices = []
277-
matches_names = [p.name for p in matches]
278-
exact_match = constraint["name"] in matches_names
279-
if exact_match:
280-
choices.append(
281-
matches[matches_names.index(constraint["name"])].pretty_name
282-
)
283-
284-
for found_package in matches:
285-
if len(choices) >= 10:
286-
break
287-
288-
if found_package.name.lower() == constraint["name"].lower():
289-
continue
298+
choices = self._generate_choice_list(matches, canonicalized_name)
290299

291-
choices.append(found_package.pretty_name)
292-
293-
self.line(
300+
info_string = (
294301
f"Found <info>{len(matches)}</info> packages matching"
295302
f" <c1>{package}</c1>"
296303
)
297304

305+
if len(matches) > 10:
306+
info_string += "\nShowing the first 10 matches"
307+
308+
self.line(info_string)
309+
298310
package = self.choice(
299311
"\nEnter package # to add, or the complete package name if it"
300312
" is not listed",

tests/console/commands/test_init.py

+54
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,22 @@
55
from pathlib import Path
66
from typing import TYPE_CHECKING
77
from typing import Iterator
8+
from typing import List
89

910
import pytest
1011

1112
from cleo.testers.command_tester import CommandTester
1213

1314
from poetry.repositories import Pool
1415
from poetry.utils._compat import decode
16+
from poetry.utils.helpers import canonicalize_name
1517
from tests.helpers import PoetryTestApplication
1618
from tests.helpers import get_package
1719

1820

1921
if TYPE_CHECKING:
22+
from _pytest.fixtures import FixtureRequest
23+
from poetry.core.packages.package import Package
2024
from pytest_mock import MockerFixture
2125

2226
from poetry.poetry import Poetry
@@ -123,6 +127,7 @@ def test_interactive_with_dependencies(tester: CommandTester, repo: "TestReposit
123127
repo.add_package(get_package("django-pendulum", "0.1.6-pre4"))
124128
repo.add_package(get_package("pendulum", "2.0.0"))
125129
repo.add_package(get_package("pytest", "3.6.0"))
130+
repo.add_package(get_package("flask", "2.0.0"))
126131

127132
inputs = [
128133
"my-package", # Package name
@@ -135,6 +140,9 @@ def test_interactive_with_dependencies(tester: CommandTester, repo: "TestReposit
135140
"pendulu", # Search for package
136141
"1", # Second option is pendulum
137142
"", # Do not set constraint
143+
"Flask",
144+
"0",
145+
"",
138146
"", # Stop searching for packages
139147
"", # Interactive dev packages
140148
"pytest", # Search for package
@@ -158,6 +166,7 @@ def test_interactive_with_dependencies(tester: CommandTester, repo: "TestReposit
158166
[tool.poetry.dependencies]
159167
python = "~2.7 || ^3.6"
160168
pendulum = "^2.0.0"
169+
flask = "^2.0.0"
161170
162171
[tool.poetry.group.dev.dependencies]
163172
pytest = "^3.6.0"
@@ -242,6 +251,51 @@ def test_interactive_with_git_dependencies(
242251
assert expected in tester.io.fetch_output()
243252

244253

254+
_generate_choice_list_packages_params: List[List["Package"]] = [
255+
[
256+
get_package("flask-blacklist", "1.0.0"),
257+
get_package("Flask-Shelve", "1.0.0"),
258+
get_package("flask-pwa", "1.0.0"),
259+
get_package("Flask-test1", "1.0.0"),
260+
get_package("Flask-test2", "1.0.0"),
261+
get_package("Flask-test3", "1.0.0"),
262+
get_package("Flask-test4", "1.0.0"),
263+
get_package("Flask-test5", "1.0.0"),
264+
get_package("Flask", "1.0.0"),
265+
get_package("Flask-test6", "1.0.0"),
266+
get_package("Flask-test7", "1.0.0"),
267+
],
268+
[
269+
get_package("flask-blacklist", "1.0.0"),
270+
get_package("Flask-Shelve", "1.0.0"),
271+
get_package("flask-pwa", "1.0.0"),
272+
get_package("Flask-test1", "1.0.0"),
273+
get_package("Flask", "1.0.0"),
274+
],
275+
]
276+
277+
278+
@pytest.fixture(params=_generate_choice_list_packages_params)
279+
def _generate_choice_list_packages(request: "FixtureRequest") -> List["Package"]:
280+
return request.param
281+
282+
283+
@pytest.mark.parametrize("package_name", ["flask", "Flask", "flAsK"])
284+
def test_generate_choice_list(
285+
tester: CommandTester,
286+
package_name: str,
287+
_generate_choice_list_packages: List["Package"],
288+
):
289+
init_command = tester.command
290+
291+
packages = _generate_choice_list_packages
292+
choices = init_command._generate_choice_list(
293+
packages, canonicalize_name(package_name)
294+
)
295+
296+
assert choices[0] == "Flask"
297+
298+
245299
def test_interactive_with_git_dependencies_with_reference(
246300
tester: CommandTester, repo: "TestRepository"
247301
):

tests/utils/test_helpers.py

+19
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from pathlib import Path
22
from typing import TYPE_CHECKING
33

4+
import pytest
5+
46
from poetry.core.utils.helpers import parse_requires
57

8+
from poetry.utils.helpers import canonicalize_name
69
from poetry.utils.helpers import get_cert
710
from poetry.utils.helpers import get_client_cert
811

@@ -79,3 +82,19 @@ def test_get_client_cert(config: "Config"):
7982
config.merge({"certificates": {"foo": {"client-cert": client_cert}}})
8083

8184
assert get_client_cert(config, "foo") == Path(client_cert)
85+
86+
87+
test_canonicalize_name_cases = [
88+
("flask", "flask"),
89+
("Flask", "flask"),
90+
("FLASK", "flask"),
91+
("FlAsK", "flask"),
92+
("fLaSk57", "flask57"),
93+
("flask-57", "flask-57"),
94+
]
95+
96+
97+
@pytest.mark.parametrize("test, expected", test_canonicalize_name_cases)
98+
def test_canonicalize_name(test: str, expected: str):
99+
canonicalized_name = canonicalize_name(test)
100+
assert canonicalized_name == expected

0 commit comments

Comments
 (0)