Skip to content

Commit 9f5082e

Browse files
authored
Merge pull request #12477 from sbidoul/fix-test-suite-setuptools69-sbi
Update legacy editables support for compatibility with setuptools >= 69
2 parents 2b439ee + 98dc4b2 commit 9f5082e

File tree

10 files changed

+89
-36
lines changed

10 files changed

+89
-36
lines changed

news/12477.feature.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make pip freeze and pip uninstall of legacy editable installs of packages whose name
2+
contains ``_`` compatible with ``setuptools>=69``.

src/pip/_internal/utils/egg_link.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,31 @@
1515
]
1616

1717

18-
def _egg_link_name(raw_name: str) -> str:
18+
def _egg_link_names(raw_name: str) -> List[str]:
1919
"""
2020
Convert a Name metadata value to a .egg-link name, by applying
2121
the same substitution as pkg_resources's safe_name function.
2222
Note: we cannot use canonicalize_name because it has a different logic.
23+
24+
We also look for the raw name (without normalization) as setuptools 69 changed
25+
the way it names .egg-link files (https://github.com/pypa/setuptools/issues/4167).
2326
"""
24-
return re.sub("[^A-Za-z0-9.]+", "-", raw_name) + ".egg-link"
27+
return [
28+
re.sub("[^A-Za-z0-9.]+", "-", raw_name) + ".egg-link",
29+
f"{raw_name}.egg-link",
30+
]
2531

2632

2733
def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]:
2834
"""
2935
Look for a .egg-link file for project name, by walking sys.path.
3036
"""
31-
egg_link_name = _egg_link_name(raw_name)
37+
egg_link_names = _egg_link_names(raw_name)
3238
for path_item in sys.path:
33-
egg_link = os.path.join(path_item, egg_link_name)
34-
if os.path.isfile(egg_link):
35-
return egg_link
39+
for egg_link_name in egg_link_names:
40+
egg_link = os.path.join(path_item, egg_link_name)
41+
if os.path.isfile(egg_link):
42+
return egg_link
3643
return None
3744

3845

@@ -64,9 +71,10 @@ def egg_link_path_from_location(raw_name: str) -> Optional[str]:
6471
sites.append(user_site)
6572
sites.append(site_packages)
6673

67-
egg_link_name = _egg_link_name(raw_name)
74+
egg_link_names = _egg_link_names(raw_name)
6875
for site in sites:
69-
egglink = os.path.join(site, egg_link_name)
70-
if os.path.isfile(egglink):
71-
return egglink
76+
for egg_link_name in egg_link_names:
77+
egglink = os.path.join(site, egg_link_name)
78+
if os.path.isfile(egglink):
79+
return egglink
7280
return None

tests/functional/test_check.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ def test_check_complicated_name_missing(script: PipTestEnvironment) -> None:
119119

120120
# Without dependency
121121
result = script.pip("install", "--no-index", package_a_path, "--no-deps")
122-
assert "Successfully installed package-A-1.0" in result.stdout, str(result)
122+
assert (
123+
"Successfully installed package_A-1.0" in result.stdout
124+
or "Successfully installed package-A-1.0" in result.stdout
125+
), str(result)
123126

124127
result = script.pip("check", expect_error=True)
125128
expected_lines = ("package-a 1.0 requires dependency-b, which is not installed.",)
@@ -142,7 +145,10 @@ def test_check_complicated_name_broken(script: PipTestEnvironment) -> None:
142145

143146
# With broken dependency
144147
result = script.pip("install", "--no-index", package_a_path, "--no-deps")
145-
assert "Successfully installed package-A-1.0" in result.stdout, str(result)
148+
assert (
149+
"Successfully installed package_A-1.0" in result.stdout
150+
or "Successfully installed package-A-1.0" in result.stdout
151+
), str(result)
146152

147153
result = script.pip(
148154
"install",
@@ -175,7 +181,10 @@ def test_check_complicated_name_clean(script: PipTestEnvironment) -> None:
175181
)
176182

177183
result = script.pip("install", "--no-index", package_a_path, "--no-deps")
178-
assert "Successfully installed package-A-1.0" in result.stdout, str(result)
184+
assert (
185+
"Successfully installed package_A-1.0" in result.stdout
186+
or "Successfully installed package-A-1.0" in result.stdout
187+
), str(result)
179188

180189
result = script.pip(
181190
"install",
@@ -203,7 +212,10 @@ def test_check_considers_conditional_reqs(script: PipTestEnvironment) -> None:
203212
)
204213

205214
result = script.pip("install", "--no-index", package_a_path, "--no-deps")
206-
assert "Successfully installed package-A-1.0" in result.stdout, str(result)
215+
assert (
216+
"Successfully installed package_A-1.0" in result.stdout
217+
or "Successfully installed package-A-1.0" in result.stdout
218+
), str(result)
207219

208220
result = script.pip("check", expect_error=True)
209221
expected_lines = ("package-a 1.0 requires dependency-b, which is not installed.",)

tests/functional/test_freeze.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def test_freeze_editable_not_vcs(script: PipTestEnvironment) -> None:
220220
# the freeze code does.
221221
expected = textwrap.dedent(
222222
f"""\
223-
...# Editable install with no version control (version-pkg==0.1)
223+
...# Editable install with no version control (version...pkg==0.1)
224224
-e {os.path.normcase(pkg_path)}
225225
..."""
226226
)
@@ -245,7 +245,7 @@ def test_freeze_editable_git_with_no_remote(
245245
# the freeze code does.
246246
expected = textwrap.dedent(
247247
f"""\
248-
...# Editable Git install with no remote (version-pkg==0.1)
248+
...# Editable Git install with no remote (version...pkg==0.1)
249249
-e {os.path.normcase(pkg_path)}
250250
..."""
251251
)
@@ -483,7 +483,7 @@ def test_freeze_git_remote(script: PipTestEnvironment) -> None:
483483
expected = os.path.normcase(
484484
textwrap.dedent(
485485
f"""
486-
...# Editable Git...(version-pkg...)...
486+
...# Editable Git...(version...pkg...)...
487487
# '{other_remote}'
488488
-e {repo_dir}...
489489
"""

tests/functional/test_install.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def test_basic_install_editable_from_svn(script: PipTestEnvironment) -> None:
358358
checkout_path = _create_test_package(script.scratch_path)
359359
repo_url = _create_svn_repo(script.scratch_path, checkout_path)
360360
result = script.pip("install", "-e", "svn+" + repo_url + "#egg=version-pkg")
361-
result.assert_installed("version-pkg", with_files=[".svn"])
361+
result.assert_installed("version_pkg", with_files=[".svn"])
362362

363363

364364
def _test_install_editable_from_git(script: PipTestEnvironment) -> None:

tests/functional/test_install_reqs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def test_install_local_editable_with_subdirectory(script: PipTestEnvironment) ->
300300
),
301301
)
302302

303-
result.assert_installed("version-subpkg", sub_dir="version_subdir")
303+
result.assert_installed("version_subpkg", sub_dir="version_subdir")
304304

305305

306306
@pytest.mark.network

tests/functional/test_install_vcs_git.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ def test_git_with_ambiguous_revs(script: PipTestEnvironment) -> None:
449449
assert "Could not find a tag or branch" not in result.stdout
450450
# it is 'version-pkg' instead of 'version_pkg' because
451451
# egg-link name is version-pkg.egg-link because it is a single .py module
452-
result.assert_installed("version-pkg", with_files=[".git"])
452+
result.assert_installed("version_pkg", with_files=[".git"])
453453

454454

455455
def test_editable__no_revision(script: PipTestEnvironment) -> None:

tests/functional/test_new_resolver.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import TYPE_CHECKING, Callable, Dict, List, Tuple
66

77
import pytest
8+
from packaging.utils import canonicalize_name
89

910
from tests.conftest import ScriptFactory
1011
from tests.lib import (
@@ -27,9 +28,13 @@ def assert_editable(script: PipTestEnvironment, *args: str) -> None:
2728
# This simply checks whether all of the listed packages have a
2829
# corresponding .egg-link file installed.
2930
# TODO: Implement a more rigorous way to test for editable installations.
30-
egg_links = {f"{arg}.egg-link" for arg in args}
31-
assert egg_links <= set(
32-
os.listdir(script.site_packages_path)
31+
egg_links = {f"{canonicalize_name(arg)}.egg-link" for arg in args}
32+
actual_egg_links = {
33+
f"{canonicalize_name(p.stem)}.egg-link"
34+
for p in script.site_packages_path.glob("*.egg-link")
35+
}
36+
assert (
37+
egg_links <= actual_egg_links
3338
), f"{args!r} not all found in {script.site_packages_path!r}"
3439

3540

@@ -1847,7 +1852,7 @@ def test_new_resolver_succeeds_on_matching_constraint_and_requirement(
18471852

18481853
script.assert_installed(test_pkg="0.1.0")
18491854
if editable:
1850-
assert_editable(script, "test-pkg")
1855+
assert_editable(script, "test_pkg")
18511856

18521857

18531858
def test_new_resolver_applies_url_constraint_to_dep(script: PipTestEnvironment) -> None:

tests/functional/test_show.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,10 @@ def test_show_required_by_packages_basic(
277277
lines = result.stdout.splitlines()
278278

279279
assert "Name: simple" in lines
280-
assert "Required-by: requires-simple" in lines
280+
assert (
281+
"Required-by: requires_simple" in lines
282+
or "Required-by: requires-simple" in lines
283+
)
281284

282285

283286
def test_show_required_by_packages_capitalized(
@@ -294,7 +297,10 @@ def test_show_required_by_packages_capitalized(
294297
lines = result.stdout.splitlines()
295298

296299
assert "Name: simple" in lines
297-
assert "Required-by: Requires-Capitalized" in lines
300+
assert (
301+
"Required-by: Requires_Capitalized" in lines
302+
or "Required-by: Requires-Capitalized" in lines
303+
)
298304

299305

300306
def test_show_required_by_packages_requiring_capitalized(
@@ -314,8 +320,13 @@ def test_show_required_by_packages_requiring_capitalized(
314320
lines = result.stdout.splitlines()
315321
print(lines)
316322

317-
assert "Name: Requires-Capitalized" in lines
318-
assert "Required-by: requires-requires-capitalized" in lines
323+
assert (
324+
"Name: Requires_Capitalized" in lines or "Name: Requires-Capitalized" in lines
325+
)
326+
assert (
327+
"Required-by: requires_requires_capitalized" in lines
328+
or "Required-by: requires-requires-capitalized" in lines
329+
)
319330

320331

321332
def test_show_skip_work_dir_pkg(script: PipTestEnvironment) -> None:

tests/lib/__init__.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from pip._internal.models.selection_prefs import SelectionPreferences
4242
from pip._internal.models.target_python import TargetPython
4343
from pip._internal.network.session import PipSession
44+
from pip._internal.utils.egg_link import _egg_link_names
4445
from tests.lib.venv import VirtualEnvironment
4546
from tests.lib.wheel import make_wheel
4647

@@ -305,6 +306,12 @@ def files_updated(self) -> FoundFiles:
305306
def files_deleted(self) -> FoundFiles:
306307
return FoundFiles(self._impl.files_deleted)
307308

309+
def _get_egg_link_path_created(self, egg_link_paths: List[str]) -> Optional[str]:
310+
for egg_link_path in egg_link_paths:
311+
if egg_link_path in self.files_created:
312+
return egg_link_path
313+
return None
314+
308315
def assert_installed(
309316
self,
310317
pkg_name: str,
@@ -320,7 +327,7 @@ def assert_installed(
320327
e = self.test_env
321328

322329
if editable:
323-
pkg_dir = e.venv / "src" / pkg_name.lower()
330+
pkg_dir = e.venv / "src" / canonicalize_name(pkg_name)
324331
# If package was installed in a sub directory
325332
if sub_dir:
326333
pkg_dir = pkg_dir / sub_dir
@@ -329,22 +336,30 @@ def assert_installed(
329336
pkg_dir = e.site_packages / pkg_name
330337

331338
if use_user_site:
332-
egg_link_path = e.user_site / f"{pkg_name}.egg-link"
339+
egg_link_paths = [
340+
e.user_site / egg_link_name
341+
for egg_link_name in _egg_link_names(pkg_name)
342+
]
333343
else:
334-
egg_link_path = e.site_packages / f"{pkg_name}.egg-link"
344+
egg_link_paths = [
345+
e.site_packages / egg_link_name
346+
for egg_link_name in _egg_link_names(pkg_name)
347+
]
335348

349+
egg_link_path_created = self._get_egg_link_path_created(egg_link_paths)
336350
if without_egg_link:
337-
if egg_link_path in self.files_created:
351+
if egg_link_path_created:
338352
raise TestFailure(
339-
f"unexpected egg link file created: {egg_link_path!r}\n{self}"
353+
f"unexpected egg link file created: {egg_link_path_created!r}\n"
354+
f"{self}"
340355
)
341356
else:
342-
if egg_link_path not in self.files_created:
357+
if not egg_link_path_created:
343358
raise TestFailure(
344-
f"expected egg link file missing: {egg_link_path!r}\n{self}"
359+
f"expected egg link file missing: {egg_link_paths!r}\n{self}"
345360
)
346361

347-
egg_link_file = self.files_created[egg_link_path]
362+
egg_link_file = self.files_created[egg_link_path_created]
348363
egg_link_contents = egg_link_file.bytes.replace(os.linesep, "\n")
349364

350365
# FIXME: I don't understand why there's a trailing . here

0 commit comments

Comments
 (0)