From 254bdf87895a9a19166811a477c3a77d8c7039ba Mon Sep 17 00:00:00 2001 From: Daniil Konovalenko Date: Sat, 7 Jan 2023 20:18:00 +0100 Subject: [PATCH 1/6] fallback to a placeholder in case dist.location is None --- src/pip/_internal/req/req_install.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 4543be34c20..dfef45fc53c 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -1,5 +1,4 @@ # The following comment should be removed at some point in the future. -# mypy: strict-optional=False import functools import logging @@ -195,7 +194,11 @@ def __str__(self) -> str: else: s = "" if self.satisfied_by is not None: - s += " in {}".format(display_path(self.satisfied_by.location)) + if self.satisfied_by.location is not None: + location = display_path(self.satisfied_by.location) + else: + location = 'memory' + s += f" in {location}" if self.comes_from: if isinstance(self.comes_from, str): comes_from: Optional[str] = self.comes_from From 8374d818ac12b35e41d79ded476b4b8050a876f1 Mon Sep 17 00:00:00 2001 From: Daniil Konovalenko Date: Sat, 7 Jan 2023 21:16:32 +0100 Subject: [PATCH 2/6] add test --- tests/functional/test_install.py | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 7b07226c90e..e39b0b7e9f2 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -2351,3 +2351,48 @@ def test_install_8559_wheel_package_present( allow_stderr_warning=False, ) assert DEPRECATION_MSG_PREFIX not in result.stderr + + +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="3.11 required to find distributions via importlib metadata" +) +def test_install_existing_memory_distribution(script: PipTestEnvironment): + sitecustomize_text = textwrap.dedent( + """ + import sys + from importlib.metadata import Distribution, DistributionFinder + + + EXAMPLE_METADATA = '''Metadata-Version: 2.1 + Name: example + Version: 1.0.0 + + ''' + + class ExampleDistribution(Distribution): + def locate_file(self, path): + return path + + def read_text(self, filename): + if filename == 'METADATA': + return EXAMPLE_METADATA + + + class CustomFinder(DistributionFinder): + def find_distributions(self, context=None): + return [ExampleDistribution()] + + + sys.meta_path.append(CustomFinder()) + """ + ) + with open(script.site_packages_path / 'sitecustomize.py', 'w') as sitecustomize_file: + sitecustomize_file.write(sitecustomize_text) + + result = script.pip( + "install", + "example" + ) + + assert "Requirement already satisfied: example in " in result.stdout From 53064079ed127974c11930db2ce12d48f9c6c901 Mon Sep 17 00:00:00 2001 From: Daniil Konovalenko Date: Sat, 7 Jan 2023 21:16:40 +0100 Subject: [PATCH 3/6] revert mypy comment --- src/pip/_internal/req/req_install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index dfef45fc53c..a50bb869b8d 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -1,4 +1,5 @@ # The following comment should be removed at some point in the future. +# mypy: strict-optional=False import functools import logging From ed7dbe9843a318f37a289eb4c1ca3b6dd2a477a6 Mon Sep 17 00:00:00 2001 From: Daniil Konovalenko Date: Sat, 7 Jan 2023 21:18:12 +0100 Subject: [PATCH 4/6] fix formatting --- src/pip/_internal/req/req_install.py | 2 +- tests/functional/test_install.py | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index a50bb869b8d..bb38ec09da4 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -198,7 +198,7 @@ def __str__(self) -> str: if self.satisfied_by.location is not None: location = display_path(self.satisfied_by.location) else: - location = 'memory' + location = "" s += f" in {location}" if self.comes_from: if isinstance(self.comes_from, str): diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index e39b0b7e9f2..79da3d709bf 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -2355,44 +2355,41 @@ def test_install_8559_wheel_package_present( @pytest.mark.skipif( sys.version_info < (3, 11), - reason="3.11 required to find distributions via importlib metadata" + reason="3.11 required to find distributions via importlib metadata", ) def test_install_existing_memory_distribution(script: PipTestEnvironment): sitecustomize_text = textwrap.dedent( """ import sys from importlib.metadata import Distribution, DistributionFinder - - + + EXAMPLE_METADATA = '''Metadata-Version: 2.1 Name: example Version: 1.0.0 - + ''' class ExampleDistribution(Distribution): def locate_file(self, path): return path - + def read_text(self, filename): if filename == 'METADATA': return EXAMPLE_METADATA - - + + class CustomFinder(DistributionFinder): def find_distributions(self, context=None): return [ExampleDistribution()] - - + + sys.meta_path.append(CustomFinder()) """ ) - with open(script.site_packages_path / 'sitecustomize.py', 'w') as sitecustomize_file: - sitecustomize_file.write(sitecustomize_text) + with open(script.site_packages_path / "sitecustomize.py", "w") as sitecustomize: + sitecustomize.write(sitecustomize_text) - result = script.pip( - "install", - "example" - ) + result = script.pip("install", "example") assert "Requirement already satisfied: example in " in result.stdout From 15b2cc993e06fbaef5f8d6e891f78ff6a131182d Mon Sep 17 00:00:00 2001 From: Daniil Konovalenko Date: Sat, 7 Jan 2023 21:19:23 +0100 Subject: [PATCH 5/6] fix mypy --- tests/functional/test_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 79da3d709bf..3fd9329bc6e 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -2357,7 +2357,7 @@ def test_install_8559_wheel_package_present( sys.version_info < (3, 11), reason="3.11 required to find distributions via importlib metadata", ) -def test_install_existing_memory_distribution(script: PipTestEnvironment): +def test_install_existing_memory_distribution(script: PipTestEnvironment) -> None: sitecustomize_text = textwrap.dedent( """ import sys From 5540331160b47e2ae4b735b0c38dc6adb00cbced Mon Sep 17 00:00:00 2001 From: Daniil Konovalenko Date: Sat, 7 Jan 2023 21:25:44 +0100 Subject: [PATCH 6/6] add a news entry --- news/11704.bugfix.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 news/11704.bugfix.rst diff --git a/news/11704.bugfix.rst b/news/11704.bugfix.rst new file mode 100644 index 00000000000..0e7902a2590 --- /dev/null +++ b/news/11704.bugfix.rst @@ -0,0 +1,2 @@ +Fix an issue when an already existing in-memory distribution would cause +exceptions in ``pip install``