From 9f8243c0963b652496b0d54c43a0194960310ca4 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Mon, 29 Jun 2026 13:17:55 -0500 Subject: [PATCH] perf(index): filter find-links entries before url conversion --- ...0c4-da3a-402b-886d-e6cecc11237a.bugfix.rst | 2 ++ src/pip/_internal/index/sources.py | 7 ++--- tests/unit/test_collector.py | 31 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 news/d54d50c4-da3a-402b-886d-e6cecc11237a.bugfix.rst diff --git a/news/d54d50c4-da3a-402b-886d-e6cecc11237a.bugfix.rst b/news/d54d50c4-da3a-402b-886d-e6cecc11237a.bugfix.rst new file mode 100644 index 0000000000..342f33f32f --- /dev/null +++ b/news/d54d50c4-da3a-402b-886d-e6cecc11237a.bugfix.rst @@ -0,0 +1,2 @@ +Improve local ``--find-links`` directory scanning performance by avoiding URL +conversion for files that are not valid package archives or HTML pages. diff --git a/src/pip/_internal/index/sources.py b/src/pip/_internal/index/sources.py index 6e7c007301..094b9f2c52 100644 --- a/src/pip/_internal/index/sources.py +++ b/src/pip/_internal/index/sources.py @@ -60,9 +60,8 @@ def _scan_directory(self) -> None: and project_name_to_urls at the same time """ for entry in os.scandir(self._path): - url = path_to_url(entry.path) - if _is_html_file(url): - self._page_candidates.append(url) + if _is_html_file(entry.name): + self._page_candidates.append(path_to_url(entry.path)) continue # File must have a valid wheel or sdist name, @@ -75,7 +74,7 @@ def _scan_directory(self) -> None: except InvalidSdistFilename: continue - self._project_name_to_urls[project_filename].append(url) + self._project_name_to_urls[project_filename].append(path_to_url(entry.path)) self._scanned_directory = True @property diff --git a/tests/unit/test_collector.py b/tests/unit/test_collector.py index b84fbdb0b3..b6a3a9baec 100644 --- a/tests/unit/test_collector.py +++ b/tests/unit/test_collector.py @@ -37,6 +37,7 @@ _ensure_quoted_url, ) from pip._internal.network.session import PipSession +from pip._internal.utils.urls import path_to_url from tests.lib import ( TestData, @@ -941,6 +942,36 @@ def check_links_include(links: list[Link], names: list[str]) -> None: ), f"name {name!r} not among links: {links}" +def test_flat_directory_source_ignores_non_package_files_before_url_conversion( + tmp_path: Path, +) -> None: + (tmp_path / "ignored file #1.txt").touch() + (tmp_path / "demo_pkg-1.0-py3-none-any.whl").touch() + (tmp_path / "page with spaces.html").touch() + + def guarded_path_to_url(path: str) -> str: + assert "ignored file" not in path + return path_to_url(path) + + with mock.patch( + "pip._internal.index.sources.path_to_url", side_effect=guarded_path_to_url + ): + source = _FlatDirectorySource( + candidates_from_page=lambda link: [ + InstallationCandidate("demo-pkg", "1.0", link) + ], + path=os.fspath(tmp_path), + project_name="demo-pkg", + ) + + file_links = list(source.file_links()) + page_candidates = list(source.page_candidates()) + + check_links_include(file_links, names=["demo_pkg-1.0-py3-none-any.whl"]) + assert len(page_candidates) == 1 + check_links_include([page_candidates[0].link], names=["page%20with%20spaces.html"]) + + class TestLinkCollector: @mock.patch("pip._internal.index.collector._get_simple_response") def test_fetch_response(self, mock_get_simple_response: mock.Mock) -> None: