Skip to content

Commit 7c6d131

Browse files
add mock server to test that each dist is downloaded exactly once
1 parent d6fdfc0 commit 7c6d131

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

tests/conftest.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,3 +992,51 @@ def html_index_for_packages(
992992
f.write(pkg_index_content)
993993

994994
return html_dir
995+
996+
997+
class OneTimeDownloadHandler(http.server.SimpleHTTPRequestHandler):
998+
"""Serve files from the current directory, but error if a file is downloaded more
999+
than once."""
1000+
1001+
_seen_paths: ClassVar[Set[str]] = set()
1002+
1003+
def do_GET(self) -> None:
1004+
if self.path in self._seen_paths:
1005+
self.send_error(
1006+
http.HTTPStatus.NOT_FOUND,
1007+
f"File {self.path} not available more than once!",
1008+
)
1009+
return
1010+
super().do_GET()
1011+
if not (self.path.endswith("/") or self.path.endswith(".metadata")):
1012+
self._seen_paths.add(self.path)
1013+
1014+
1015+
@pytest.fixture(scope="function")
1016+
def html_index_with_onetime_server(
1017+
html_index_for_packages: Path,
1018+
) -> Iterator[http.server.ThreadingHTTPServer]:
1019+
"""Serve files from a generated pypi index, erroring if a file is downloaded more
1020+
than once.
1021+
1022+
Provide `-i http://localhost:8000` to pip invocations to point them at this server.
1023+
"""
1024+
1025+
class InDirectoryServer(http.server.ThreadingHTTPServer):
1026+
def finish_request(self, request: Any, client_address: Any) -> None:
1027+
self.RequestHandlerClass(
1028+
request, client_address, self, directory=str(html_index_for_packages) # type: ignore[call-arg] # noqa: E501
1029+
)
1030+
1031+
class Handler(OneTimeDownloadHandler):
1032+
_seen_paths: ClassVar[Set[str]] = set()
1033+
1034+
with InDirectoryServer(("", 8000), Handler) as httpd:
1035+
server_thread = threading.Thread(target=httpd.serve_forever)
1036+
server_thread.start()
1037+
1038+
try:
1039+
yield httpd
1040+
finally:
1041+
httpd.shutdown()
1042+
server_thread.join()

tests/functional/test_download.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,57 @@ def test_download_metadata(
13241324
assert sorted(os.listdir(download_dir)) == expected_outputs
13251325

13261326

1327+
@pytest.mark.parametrize(
1328+
"requirement_to_download, expected_outputs, doubled_path",
1329+
[
1330+
(
1331+
"simple2==1.0",
1332+
["simple-1.0.tar.gz", "simple2-1.0.tar.gz"],
1333+
"/simple2/simple2-1.0.tar.gz",
1334+
),
1335+
("simple==2.0", ["simple-2.0.tar.gz"], "/simple/simple-2.0.tar.gz"),
1336+
(
1337+
"colander",
1338+
["colander-0.9.9-py2.py3-none-any.whl", "translationstring-1.1.tar.gz"],
1339+
"/colander/colander-0.9.9-py2.py3-none-any.whl",
1340+
),
1341+
(
1342+
"compilewheel",
1343+
[
1344+
"compilewheel-1.0-py2.py3-none-any.whl",
1345+
"simple-1.0.tar.gz",
1346+
],
1347+
"/compilewheel/compilewheel-1.0-py2.py3-none-any.whl",
1348+
),
1349+
],
1350+
)
1351+
def test_download_metadata_server(
1352+
download_server_html_index: Callable[..., Tuple[TestPipResult, Path]],
1353+
requirement_to_download: str,
1354+
expected_outputs: List[str],
1355+
doubled_path: str,
1356+
) -> None:
1357+
"""Verify that if a data-dist-info-metadata attribute is present, then it is used
1358+
instead of the actual dist's METADATA.
1359+
1360+
Additionally, verify that each dist is downloaded exactly once using a mock server.
1361+
1362+
This is a regression test for issue https://github.com/pypa/pip/issues/11847.
1363+
"""
1364+
_, download_dir = download_server_html_index(
1365+
[requirement_to_download, "--no-cache-dir"],
1366+
)
1367+
assert sorted(os.listdir(download_dir)) == expected_outputs
1368+
shutil.rmtree(download_dir)
1369+
result, _ = download_server_html_index(
1370+
[requirement_to_download, "--no-cache-dir"],
1371+
allow_error=True,
1372+
)
1373+
assert result.returncode != 0
1374+
expected_msg = f"File {doubled_path} not available more than once!"
1375+
assert expected_msg in result.stderr
1376+
1377+
13271378
@pytest.mark.parametrize(
13281379
"requirement_to_download, real_hash",
13291380
[

0 commit comments

Comments
 (0)