mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
42359a9605
The pip-specific Path implementation has been removed, and all its usages replaced by pathlib.Path. The tmpdir and tmpdir_factory fixtures are also removed, and all usages are replaced by tmp_path and tmp_path_factory, which use pathlib.Path. The pip() function now also accepts pathlib.Path so we don't need to put str() everywhere. Path arguments are coerced with os.fspath() into str.
134 lines
4.5 KiB
Python
134 lines
4.5 KiB
Python
import os
|
|
import shutil
|
|
from pathlib import Path
|
|
from shutil import rmtree
|
|
from tempfile import mkdtemp
|
|
from typing import Any, Dict
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
|
|
from pip._internal.exceptions import HashMismatch
|
|
from pip._internal.models.link import Link
|
|
from pip._internal.network.download import Downloader
|
|
from pip._internal.network.session import PipSession
|
|
from pip._internal.operations.prepare import unpack_url
|
|
from pip._internal.utils.hashes import Hashes
|
|
from tests.lib import TestData
|
|
from tests.lib.requests_mocks import MockResponse
|
|
|
|
|
|
def test_unpack_url_with_urllib_response_without_content_type(data: TestData) -> None:
|
|
"""
|
|
It should download and unpack files even if no Content-Type header exists
|
|
"""
|
|
_real_session = PipSession()
|
|
|
|
def _fake_session_get(*args: Any, **kwargs: Any) -> Dict[str, str]:
|
|
resp = _real_session.get(*args, **kwargs)
|
|
del resp.headers["Content-Type"]
|
|
return resp
|
|
|
|
session = Mock()
|
|
session.get = _fake_session_get
|
|
download = Downloader(session, progress_bar="on")
|
|
|
|
uri = data.packages.joinpath("simple-1.0.tar.gz").as_uri()
|
|
link = Link(uri)
|
|
temp_dir = mkdtemp()
|
|
try:
|
|
unpack_url(
|
|
link,
|
|
temp_dir,
|
|
download=download,
|
|
download_dir=None,
|
|
verbosity=0,
|
|
)
|
|
assert set(os.listdir(temp_dir)) == {
|
|
"PKG-INFO",
|
|
"setup.cfg",
|
|
"setup.py",
|
|
"simple",
|
|
"simple.egg-info",
|
|
}
|
|
finally:
|
|
rmtree(temp_dir)
|
|
|
|
|
|
@patch("pip._internal.network.download.raise_for_status")
|
|
def test_download_http_url__no_directory_traversal(
|
|
mock_raise_for_status: Mock, tmpdir: Path
|
|
) -> None:
|
|
"""
|
|
Test that directory traversal doesn't happen on download when the
|
|
Content-Disposition header contains a filename with a ".." path part.
|
|
"""
|
|
mock_url = "http://www.example.com/whatever.tgz"
|
|
contents = b"downloaded"
|
|
link = Link(mock_url)
|
|
|
|
session = Mock()
|
|
resp = MockResponse(contents)
|
|
resp.url = mock_url
|
|
resp.headers = {
|
|
# Set the content-type to a random value to prevent
|
|
# mimetypes.guess_extension from guessing the extension.
|
|
"content-type": "random",
|
|
"content-disposition": 'attachment;filename="../out_dir_file"',
|
|
}
|
|
session.get.return_value = resp
|
|
download = Downloader(session, progress_bar="on")
|
|
|
|
download_dir = os.fspath(tmpdir.joinpath("download"))
|
|
os.mkdir(download_dir)
|
|
file_path, content_type = download(link, download_dir)
|
|
# The file should be downloaded to download_dir.
|
|
actual = os.listdir(download_dir)
|
|
assert actual == ["out_dir_file"]
|
|
mock_raise_for_status.assert_called_once_with(resp)
|
|
|
|
|
|
@pytest.fixture
|
|
def clean_project(tmpdir_factory: pytest.TempPathFactory, data: TestData) -> Path:
|
|
tmpdir = tmpdir_factory.mktemp("clean_project")
|
|
new_project_dir = tmpdir.joinpath("FSPkg")
|
|
path = data.packages.joinpath("FSPkg")
|
|
shutil.copytree(path, new_project_dir)
|
|
return new_project_dir
|
|
|
|
|
|
class Test_unpack_url:
|
|
def prep(self, tmpdir: Path, data: TestData) -> None:
|
|
self.build_dir = os.fspath(tmpdir.joinpath("build"))
|
|
self.download_dir = tmpdir.joinpath("download")
|
|
os.mkdir(self.build_dir)
|
|
os.mkdir(self.download_dir)
|
|
self.dist_file = "simple-1.0.tar.gz"
|
|
self.dist_file2 = "simple-2.0.tar.gz"
|
|
self.dist_path = data.packages.joinpath(self.dist_file)
|
|
self.dist_path2 = data.packages.joinpath(self.dist_file2)
|
|
self.dist_url = Link(self.dist_path.as_uri())
|
|
self.dist_url2 = Link(self.dist_path2.as_uri())
|
|
self.no_download = Mock(side_effect=AssertionError)
|
|
|
|
def test_unpack_url_no_download(self, tmpdir: Path, data: TestData) -> None:
|
|
self.prep(tmpdir, data)
|
|
unpack_url(self.dist_url, self.build_dir, self.no_download, verbosity=0)
|
|
assert os.path.isdir(os.path.join(self.build_dir, "simple"))
|
|
assert not os.path.isfile(os.path.join(self.download_dir, self.dist_file))
|
|
|
|
def test_unpack_url_bad_hash(self, tmpdir: Path, data: TestData) -> None:
|
|
"""
|
|
Test when the file url hash fragment is wrong
|
|
"""
|
|
self.prep(tmpdir, data)
|
|
url = f"{self.dist_url.url}#md5=bogus"
|
|
dist_url = Link(url)
|
|
with pytest.raises(HashMismatch):
|
|
unpack_url(
|
|
dist_url,
|
|
self.build_dir,
|
|
download=self.no_download,
|
|
hashes=Hashes({"md5": ["bogus"]}),
|
|
verbosity=0,
|
|
)
|