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.
180 lines
6.3 KiB
Python
180 lines
6.3 KiB
Python
"""
|
|
locations.py tests
|
|
|
|
"""
|
|
import getpass
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import sysconfig
|
|
import tempfile
|
|
from pathlib import Path
|
|
from typing import Any, Dict
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from pip._internal.locations import SCHEME_KEYS, _should_use_sysconfig, get_scheme
|
|
|
|
if sys.platform == "win32":
|
|
pwd = Mock()
|
|
else:
|
|
import pwd
|
|
|
|
|
|
def _get_scheme_dict(*args: Any, **kwargs: Any) -> Dict[str, str]:
|
|
scheme = get_scheme(*args, **kwargs)
|
|
return {k: getattr(scheme, k) for k in SCHEME_KEYS}
|
|
|
|
|
|
class TestLocations:
|
|
def setup(self) -> None:
|
|
self.tempdir = tempfile.mkdtemp()
|
|
self.st_uid = 9999
|
|
self.username = "example"
|
|
self.patch()
|
|
|
|
def teardown(self) -> None:
|
|
self.revert_patch()
|
|
shutil.rmtree(self.tempdir, ignore_errors=True)
|
|
|
|
def patch(self) -> None:
|
|
"""first store and then patch python methods pythons"""
|
|
self.tempfile_gettempdir = tempfile.gettempdir
|
|
self.old_os_fstat = os.fstat
|
|
if sys.platform != "win32":
|
|
# os.geteuid and pwd.getpwuid are not implemented on windows
|
|
self.old_os_geteuid = os.geteuid
|
|
self.old_pwd_getpwuid = pwd.getpwuid
|
|
self.old_getpass_getuser = getpass.getuser
|
|
|
|
# now patch
|
|
tempfile.gettempdir = lambda: self.tempdir
|
|
getpass.getuser = lambda: self.username
|
|
os.fstat = lambda fd: self.get_mock_fstat(fd)
|
|
if sys.platform != "win32":
|
|
os.geteuid = lambda: self.st_uid
|
|
pwd.getpwuid = self.get_mock_getpwuid
|
|
|
|
def revert_patch(self) -> None:
|
|
"""revert the patches to python methods"""
|
|
tempfile.gettempdir = self.tempfile_gettempdir
|
|
getpass.getuser = self.old_getpass_getuser
|
|
if sys.platform != "win32":
|
|
# os.geteuid and pwd.getpwuid are not implemented on windows
|
|
os.geteuid = self.old_os_geteuid
|
|
pwd.getpwuid = self.old_pwd_getpwuid
|
|
os.fstat = self.old_os_fstat
|
|
|
|
def get_mock_fstat(self, fd: int) -> os.stat_result:
|
|
"""returns a basic mock fstat call result.
|
|
Currently only the st_uid attribute has been set.
|
|
"""
|
|
result = Mock()
|
|
result.st_uid = self.st_uid
|
|
return result
|
|
|
|
def get_mock_getpwuid(self, uid: int) -> Any:
|
|
"""returns a basic mock pwd.getpwuid call result.
|
|
Currently only the pw_name attribute has been set.
|
|
"""
|
|
result = Mock()
|
|
result.pw_name = self.username
|
|
return result
|
|
|
|
def test_default_should_use_sysconfig(
|
|
self, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
monkeypatch.delattr(sysconfig, "_PIP_USE_SYSCONFIG", raising=False)
|
|
if sys.version_info[:2] >= (3, 10):
|
|
assert _should_use_sysconfig() is True
|
|
else:
|
|
assert _should_use_sysconfig() is False
|
|
|
|
@pytest.mark.parametrize("vendor_value", [True, False, None, "", 0, 1])
|
|
def test_vendor_overriden_should_use_sysconfig(
|
|
self, monkeypatch: pytest.MonkeyPatch, vendor_value: Any
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
sysconfig, "_PIP_USE_SYSCONFIG", vendor_value, raising=False
|
|
)
|
|
assert _should_use_sysconfig() is bool(vendor_value)
|
|
|
|
|
|
class TestDistutilsScheme:
|
|
def test_root_modifies_appropriately(self) -> None:
|
|
# This deals with nt/posix path differences
|
|
# root is c:\somewhere\else or /somewhere/else
|
|
root = os.path.normcase(
|
|
os.path.abspath(os.path.join(os.path.sep, "somewhere", "else"))
|
|
)
|
|
norm_scheme = _get_scheme_dict("example")
|
|
root_scheme = _get_scheme_dict("example", root=root)
|
|
|
|
for key, value in norm_scheme.items():
|
|
drive, path = os.path.splitdrive(os.path.abspath(value))
|
|
expected = os.path.join(root, path[1:])
|
|
assert os.path.abspath(root_scheme[key]) == expected
|
|
|
|
@pytest.mark.incompatible_with_sysconfig
|
|
@pytest.mark.incompatible_with_venv
|
|
def test_distutils_config_file_read(
|
|
self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
# This deals with nt/posix path differences
|
|
install_scripts = os.path.normcase(
|
|
os.path.abspath(os.path.join(os.path.sep, "somewhere", "else"))
|
|
)
|
|
f = tmpdir / "config" / "setup.cfg"
|
|
f.parent.mkdir()
|
|
f.write_text("[install]\ninstall-scripts=" + install_scripts)
|
|
from distutils.dist import Distribution
|
|
|
|
# patch the function that returns what config files are present
|
|
monkeypatch.setattr(
|
|
Distribution,
|
|
"find_config_files",
|
|
lambda self: [f],
|
|
)
|
|
scheme = _get_scheme_dict("example")
|
|
assert scheme["scripts"] == install_scripts
|
|
|
|
@pytest.mark.incompatible_with_sysconfig
|
|
@pytest.mark.incompatible_with_venv
|
|
# when we request install-lib, we should install everything (.py &
|
|
# .so) into that path; i.e. ensure platlib & purelib are set to
|
|
# this path. sysconfig does not support this.
|
|
def test_install_lib_takes_precedence(
|
|
self, tmpdir: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
# This deals with nt/posix path differences
|
|
install_lib = os.path.normcase(
|
|
os.path.abspath(os.path.join(os.path.sep, "somewhere", "else"))
|
|
)
|
|
f = tmpdir / "config" / "setup.cfg"
|
|
f.parent.mkdir()
|
|
f.write_text("[install]\ninstall-lib=" + install_lib)
|
|
from distutils.dist import Distribution
|
|
|
|
# patch the function that returns what config files are present
|
|
monkeypatch.setattr(
|
|
Distribution,
|
|
"find_config_files",
|
|
lambda self: [f],
|
|
)
|
|
scheme = _get_scheme_dict("example")
|
|
assert scheme["platlib"] == install_lib + os.path.sep
|
|
assert scheme["purelib"] == install_lib + os.path.sep
|
|
|
|
def test_prefix_modifies_appropriately(self) -> None:
|
|
prefix = os.path.abspath(os.path.join("somewhere", "else"))
|
|
|
|
normal_scheme = _get_scheme_dict("example")
|
|
prefix_scheme = _get_scheme_dict("example", prefix=prefix)
|
|
|
|
def _calculate_expected(value: str) -> str:
|
|
path = os.path.join(prefix, os.path.relpath(value, sys.prefix))
|
|
return os.path.normpath(path)
|
|
|
|
expected = {k: _calculate_expected(v) for k, v in normal_scheme.items()}
|
|
assert prefix_scheme == expected
|