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

Use safe_extra to standardize extra dependencies #4122

Merged
merged 2 commits into from
Jun 7, 2022
Merged
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
6 changes: 5 additions & 1 deletion src/poetry/puzzle/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from poetry.puzzle.exceptions import OverrideNeeded
from poetry.repositories.exceptions import PackageNotFound
from poetry.utils.helpers import download_file
from poetry.utils.helpers import safe_extra
from poetry.vcs.git import Git


Expand Down Expand Up @@ -563,6 +564,7 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage:
# to the current package
if package.dependency.extras:
for extra in package.dependency.extras:
extra = safe_extra(extra)
if extra not in package.extras:
continue

Expand All @@ -585,7 +587,9 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage:
(dep.is_optional() and dep.name not in optional_dependencies)
or (
dep.in_extras
and not set(dep.in_extras).intersection(package.dependency.extras)
and not set(dep.in_extras).intersection(
{safe_extra(extra) for extra in package.dependency.extras}
)
)
):
continue
Expand Down
12 changes: 12 additions & 0 deletions src/poetry/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,15 @@ def pluralize(count: int, word: str = "") -> str:
if count == 1:
return word
return word + "s"


def safe_extra(extra: str) -> str:
"""Convert an arbitrary string to a standard 'extra' name.

Any runs of non-alphanumeric characters are replaced with a single '_',
and the result is always lowercased.

See
https://github.com/pypa/setuptools/blob/452e13c/pkg_resources/__init__.py#L1423-L1431.
"""
return re.sub("[^A-Za-z0-9.-]+", "_", extra).lower()
52 changes: 36 additions & 16 deletions tests/console/commands/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,12 @@ def test_add_greater_constraint(
assert tester.command.installer.executor.installations_count == 1


@pytest.mark.parametrize("extra_name", ["msgpack", "MsgPack"])
def test_add_constraint_with_extras(
app: PoetryTestApplication, repo: TestRepository, tester: CommandTester
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester,
extra_name: str,
):
cachy1 = get_package("cachy", "0.1.0")
cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]}
Expand All @@ -212,7 +216,7 @@ def test_add_constraint_with_extras(
repo.add_package(cachy1)
repo.add_package(get_package("msgpack-python", "0.5.3"))

tester.execute("cachy[msgpack]>=0.1.0,<0.2.0")
tester.execute(f"cachy[{extra_name}]>=0.1.0,<0.2.0")

expected = """\

Expand Down Expand Up @@ -327,19 +331,21 @@ def test_add_git_constraint_with_poetry(
assert tester.command.installer.executor.installations_count == 2


@pytest.mark.parametrize("extra_name", ["foo", "FOO"])
def test_add_git_constraint_with_extras(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester,
tmp_venv: VirtualEnv,
extra_name: str,
):
tester.command.set_env(tmp_venv)

repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))

tester.execute("git+https://github.com/demo/demo.git[foo,bar]")
tester.execute(f"git+https://github.com/demo/demo.git[{extra_name},bar]")

expected = """\

Expand All @@ -364,7 +370,7 @@ def test_add_git_constraint_with_extras(
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "https://github.com/demo/demo.git",
"extras": ["foo", "bar"],
"extras": [extra_name, "bar"],
}


Expand Down Expand Up @@ -562,8 +568,12 @@ def test_add_file_constraint_sdist(
assert content["dependencies"]["demo"] == {"path": path}


@pytest.mark.parametrize("extra_name", ["msgpack", "MsgPack"])
def test_add_constraint_with_extras_option(
app: PoetryTestApplication, repo: TestRepository, tester: CommandTester
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester,
extra_name: str,
):
cachy2 = get_package("cachy", "0.2.0")
cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]}
Expand All @@ -574,7 +584,7 @@ def test_add_constraint_with_extras_option(
repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3"))

tester.execute("cachy=0.2.0 --extras msgpack")
tester.execute(f"cachy=0.2.0 --extras {extra_name}")

expected = """\

Expand All @@ -597,7 +607,7 @@ def test_add_constraint_with_extras_option(
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"extras": ["msgpack"],
"extras": [extra_name],
}


Expand Down Expand Up @@ -641,10 +651,12 @@ def test_add_url_constraint_wheel(
}


@pytest.mark.parametrize("extra_name", ["foo", "FOO"])
def test_add_url_constraint_wheel_with_extras(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester,
extra_name: str,
mocker: MockerFixture,
):
repo.add_package(get_package("pendulum", "1.4.4"))
Expand All @@ -653,7 +665,7 @@ def test_add_url_constraint_wheel_with_extras(

tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
"[foo,bar]"
f"[{extra_name},bar]"
)

expected = """\
Expand Down Expand Up @@ -684,7 +696,7 @@ def test_add_url_constraint_wheel_with_extras(
"url": (
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
),
"extras": ["foo", "bar"],
"extras": [extra_name, "bar"],
}


Expand Down Expand Up @@ -1165,11 +1177,13 @@ def test_add_greater_constraint_old_installer(
assert len(installer.installs) == 1


@pytest.mark.parametrize("extra_name", ["msgpack", "MsgPack"])
def test_add_constraint_with_extras_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
):
cachy1 = get_package("cachy", "0.1.0")
cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]}
Expand All @@ -1180,7 +1194,7 @@ def test_add_constraint_with_extras_old_installer(
repo.add_package(cachy1)
repo.add_package(get_package("msgpack-python", "0.5.3"))

old_tester.execute("cachy[msgpack]>=0.1.0,<0.2.0")
old_tester.execute(f"cachy[{extra_name}]>=0.1.0,<0.2.0")

expected = """\

Expand Down Expand Up @@ -1298,17 +1312,19 @@ def test_add_git_constraint_with_poetry_old_installer(
assert len(installer.installs) == 2


@pytest.mark.parametrize("extra_name", ["foo", "FOO"])
def test_add_git_constraint_with_extras_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
):
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))

old_tester.execute("git+https://github.com/demo/demo.git[foo,bar]")
old_tester.execute(f"git+https://github.com/demo/demo.git[{extra_name},bar]")

expected = """\

Expand All @@ -1334,7 +1350,7 @@ def test_add_git_constraint_with_extras_old_installer(
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "https://github.com/demo/demo.git",
"extras": ["foo", "bar"],
"extras": [extra_name, "bar"],
}


Expand Down Expand Up @@ -1523,11 +1539,13 @@ def test_add_file_constraint_sdist_old_installer(
assert content["dependencies"]["demo"] == {"path": path}


@pytest.mark.parametrize("extra_name", ["msgpack", "MsgPack"])
def test_add_constraint_with_extras_option_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
):
cachy2 = get_package("cachy", "0.2.0")
cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]}
Expand All @@ -1538,7 +1556,7 @@ def test_add_constraint_with_extras_option_old_installer(
repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3"))

old_tester.execute("cachy=0.2.0 --extras msgpack")
old_tester.execute(f"cachy=0.2.0 --extras {extra_name}")

expected = """\

Expand All @@ -1562,7 +1580,7 @@ def test_add_constraint_with_extras_option_old_installer(
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"extras": ["msgpack"],
"extras": [extra_name],
}


Expand Down Expand Up @@ -1608,19 +1626,21 @@ def test_add_url_constraint_wheel_old_installer(
}


@pytest.mark.parametrize("extra_name", ["foo", "FOO"])
def test_add_url_constraint_wheel_with_extras_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
extra_name: str,
):
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))

old_tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
"[foo,bar]"
f"[{extra_name},bar]"
)

expected = """\
Expand Down Expand Up @@ -1650,7 +1670,7 @@ def test_add_url_constraint_wheel_with_extras_old_installer(
"url": (
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
),
"extras": ["foo", "bar"],
"extras": [extra_name, "bar"],
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ python-versions = "*"
B = {version = "^1.0", optional = true, extras = ["C"]}

[package.extras]
B = ["B[C] (>=1.0,<2.0)"]
b = ["B[C] (>=1.0,<2.0)"]

[[package]]
name = "B"
Expand All @@ -24,7 +24,7 @@ python-versions = "*"
C = {version = "^1.0", optional = true}

[package.extras]
C = ["C (>=1.0,<2.0)"]
c = ["C (>=1.0,<2.0)"]

[[package]]
name = "C"
Expand Down
4 changes: 2 additions & 2 deletions tests/installation/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1001,11 +1001,11 @@ def test_run_with_dependencies_nested_extras(
)
dependency_a = Factory.create_dependency("A", {"version": "^1.0", "extras": ["B"]})

package_b.extras = {"C": [dependency_c]}
package_b.extras = {"c": [dependency_c]}
package_b.add_dependency(dependency_c)

package_a.add_dependency(dependency_b)
package_a.extras = {"B": [dependency_b]}
package_a.extras = {"b": [dependency_b]}

repo.add_package(package_a)
repo.add_package(package_b)
Expand Down
8 changes: 8 additions & 0 deletions tests/utils/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from poetry.core.utils.helpers import parse_requires

from poetry.utils.helpers import canonicalize_name
from poetry.utils.helpers import safe_extra


def test_parse_requires():
Expand Down Expand Up @@ -77,3 +78,10 @@ def test_parse_requires():
def test_canonicalize_name(test: str, expected: str):
canonicalized_name = canonicalize_name(test)
assert canonicalized_name == expected


def test_safe_extra():
extra = "pandas.CSVDataSet"
result = safe_extra(extra)
expected = "pandas.csvdataset"
assert result == expected