summaryrefslogtreecommitdiff
path: root/tests/lib/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lib/__init__.py')
-rw-r--r--tests/lib/__init__.py638
1 files changed, 375 insertions, 263 deletions
diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py
index ddb4bfe4c..7410072f5 100644
--- a/tests/lib/__init__.py
+++ b/tests/lib/__init__.py
@@ -1,5 +1,6 @@
import json
import os
+import pathlib
import re
import shutil
import site
@@ -11,12 +12,25 @@ from contextlib import contextmanager
from hashlib import sha256
from io import BytesIO
from textwrap import dedent
-from typing import List, Optional
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Mapping,
+ Optional,
+ Tuple,
+ Union,
+ cast,
+)
from zipfile import ZipFile
import pytest
from pip._vendor.packaging.utils import canonicalize_name
-from scripttest import FoundDir, TestFileEnvironment
+from scripttest import FoundDir, FoundFile, ProcResult, TestFileEnvironment
from pip._internal.index.collector import LinkCollector
from pip._internal.index.package_finder import PackageFinder
@@ -25,50 +39,33 @@ from pip._internal.models.search_scope import SearchScope
from pip._internal.models.selection_prefs import SelectionPreferences
from pip._internal.models.target_python import TargetPython
from pip._internal.network.session import PipSession
-from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX
-from tests.lib.path import Path, curdir
+from tests.lib.venv import VirtualEnvironment
from tests.lib.wheel import make_wheel
-DATA_DIR = Path(__file__).parent.parent.joinpath("data").resolve()
-SRC_DIR = Path(__file__).resolve().parent.parent.parent
+if TYPE_CHECKING:
+ # Literal was introduced in Python 3.8.
+ from typing import Literal
-pyversion = get_major_minor_version()
+ ResolverVariant = Literal["resolvelib", "legacy"]
+else:
+ ResolverVariant = str
-CURRENT_PY_VERSION_INFO = sys.version_info[:3]
+DATA_DIR = pathlib.Path(__file__).parent.parent.joinpath("data").resolve()
+SRC_DIR = pathlib.Path(__file__).resolve().parent.parent.parent
+pyversion = get_major_minor_version()
-def assert_paths_equal(actual, expected):
- assert os.path.normpath(actual) == os.path.normpath(expected)
+CURRENT_PY_VERSION_INFO = sys.version_info[:3]
+_Test = Callable[..., None]
+_FilesState = Dict[str, Union[FoundDir, FoundFile]]
-def path_to_url(path):
- """
- Convert a path to URI. The path will be made absolute and
- will not have quoted path parts.
- (adapted from pip.util)
- """
- path = os.path.normpath(os.path.abspath(path))
- drive, path = os.path.splitdrive(path)
- filepath = path.split(os.path.sep)
- url = "/".join(filepath)
- if drive:
- # Note: match urllib.request.pathname2url's
- # behavior: uppercase the drive letter.
- return "file:///" + drive.upper() + url
- return "file://" + url
-
-
-def _test_path_to_file_url(path):
- """
- Convert a test Path to a "file://" URL.
- Args:
- path: a tests.lib.path.Path object.
- """
- return "file://" + path.resolve().replace("\\", "/")
+def assert_paths_equal(actual: str, expected: str) -> None:
+ assert os.path.normpath(actual) == os.path.normpath(expected)
-def create_file(path, contents=None):
+def create_file(path: str, contents: Optional[str] = None) -> None:
"""Create a file on the path, with the given contents"""
from pip._internal.utils.misc import ensure_dir
@@ -81,23 +78,26 @@ def create_file(path, contents=None):
def make_test_search_scope(
- find_links=None, # type: Optional[List[str]]
- index_urls=None, # type: Optional[List[str]]
-):
+ find_links: Optional[List[str]] = None,
+ index_urls: Optional[List[str]] = None,
+) -> SearchScope:
if find_links is None:
find_links = []
if index_urls is None:
index_urls = []
- return SearchScope.create(find_links=find_links, index_urls=index_urls)
+ return SearchScope.create(
+ find_links=find_links,
+ index_urls=index_urls,
+ no_index=False,
+ )
def make_test_link_collector(
- find_links=None, # type: Optional[List[str]]
- index_urls=None, # type: Optional[List[str]]
- session=None, # type: Optional[PipSession]
-):
- # type: (...) -> LinkCollector
+ find_links: Optional[List[str]] = None,
+ index_urls: Optional[List[str]] = None,
+ session: Optional[PipSession] = None,
+) -> LinkCollector:
"""
Create a LinkCollector object for testing purposes.
"""
@@ -113,13 +113,12 @@ def make_test_link_collector(
def make_test_finder(
- find_links=None, # type: Optional[List[str]]
- index_urls=None, # type: Optional[List[str]]
- allow_all_prereleases=False, # type: bool
- session=None, # type: Optional[PipSession]
- target_python=None, # type: Optional[TargetPython]
-):
- # type: (...) -> PackageFinder
+ find_links: Optional[List[str]] = None,
+ index_urls: Optional[List[str]] = None,
+ allow_all_prereleases: bool = False,
+ session: Optional[PipSession] = None,
+ target_python: Optional[TargetPython] = None,
+) -> PackageFinder:
"""
Create a PackageFinder for testing purposes.
"""
@@ -152,17 +151,23 @@ class TestData:
data into a directory and operating on the copied data.
"""
- def __init__(self, root, source=None):
+ __test__ = False
+
+ def __init__(
+ self,
+ root: pathlib.Path,
+ source: Optional[pathlib.Path] = None,
+ ) -> None:
self.source = source or DATA_DIR
- self.root = Path(root).resolve()
+ self.root = root.resolve()
@classmethod
- def copy(cls, root):
+ def copy(cls, root: pathlib.Path) -> "TestData":
obj = cls(root)
obj.reset()
return obj
- def reset(self):
+ def reset(self) -> None:
# Check explicitly for the target directory to avoid overly-broad
# try/except.
if self.root.exists():
@@ -170,51 +175,51 @@ class TestData:
shutil.copytree(self.source, self.root, symlinks=True)
@property
- def packages(self):
+ def packages(self) -> pathlib.Path:
return self.root.joinpath("packages")
@property
- def packages2(self):
+ def packages2(self) -> pathlib.Path:
return self.root.joinpath("packages2")
@property
- def packages3(self):
+ def packages3(self) -> pathlib.Path:
return self.root.joinpath("packages3")
@property
- def src(self):
+ def src(self) -> pathlib.Path:
return self.root.joinpath("src")
@property
- def indexes(self):
+ def indexes(self) -> pathlib.Path:
return self.root.joinpath("indexes")
@property
- def reqfiles(self):
+ def reqfiles(self) -> pathlib.Path:
return self.root.joinpath("reqfiles")
@property
- def completion_paths(self):
+ def completion_paths(self) -> pathlib.Path:
return self.root.joinpath("completion_paths")
@property
- def find_links(self):
- return path_to_url(self.packages)
+ def find_links(self) -> str:
+ return self.packages.as_uri()
@property
- def find_links2(self):
- return path_to_url(self.packages2)
+ def find_links2(self) -> str:
+ return self.packages2.as_uri()
@property
- def find_links3(self):
- return path_to_url(self.packages3)
+ def find_links3(self) -> str:
+ return self.packages3.as_uri()
@property
- def backends(self):
- return path_to_url(self.root.joinpath("backends"))
+ def backends(self) -> str:
+ return self.root.joinpath("backends").as_uri()
- def index_url(self, index="simple"):
- return path_to_url(self.root.joinpath("indexes", index))
+ def index_url(self, index: str = "simple") -> str:
+ return self.root.joinpath("indexes", index).as_uri()
class TestFailure(AssertionError):
@@ -222,11 +227,39 @@ class TestFailure(AssertionError):
An "assertion" failed during testing.
"""
- pass
+
+StrPath = Union[str, pathlib.Path]
+
+
+class FoundFiles(Mapping[StrPath, FoundFile]):
+ def __init__(self, paths: Mapping[str, FoundFile]) -> None:
+ self._paths = {pathlib.Path(k): v for k, v in paths.items()}
+
+ def __contains__(self, o: object) -> bool:
+ if isinstance(o, pathlib.Path):
+ return o in self._paths
+ elif isinstance(o, str):
+ return pathlib.Path(o) in self._paths
+ return False
+
+ def __len__(self) -> int:
+ return len(self._paths)
+
+ def __getitem__(self, k: StrPath) -> FoundFile:
+ if isinstance(k, pathlib.Path):
+ return self._paths[k]
+ elif isinstance(k, str):
+ return self._paths[pathlib.Path(k)]
+ raise KeyError(k)
+
+ def __iter__(self) -> Iterator[pathlib.Path]:
+ return iter(self._paths)
class TestPipResult:
- def __init__(self, impl, verbose=False):
+ __test__ = False
+
+ def __init__(self, impl: ProcResult, verbose: bool = False) -> None:
self._impl = impl
if verbose:
@@ -236,38 +269,50 @@ class TestPipResult:
print(self.stderr)
print("=======================")
- def __getattr__(self, attr):
+ def __getattr__(self, attr: str) -> Any:
return getattr(self._impl, attr)
if sys.platform == "win32":
@property
- def stdout(self):
+ def stdout(self) -> str:
return self._impl.stdout.replace("\r\n", "\n")
@property
- def stderr(self):
+ def stderr(self) -> str:
return self._impl.stderr.replace("\r\n", "\n")
- def __str__(self):
+ def __str__(self) -> str:
return str(self._impl).replace("\r\n", "\n")
else:
# Python doesn't automatically forward __str__ through __getattr__
- def __str__(self):
+ def __str__(self) -> str:
return str(self._impl)
+ @property
+ def files_created(self) -> FoundFiles:
+ return FoundFiles(self._impl.files_created)
+
+ @property
+ def files_updated(self) -> FoundFiles:
+ return FoundFiles(self._impl.files_updated)
+
+ @property
+ def files_deleted(self) -> FoundFiles:
+ return FoundFiles(self._impl.files_deleted)
+
def assert_installed(
self,
- pkg_name,
- editable=True,
- with_files=None,
- without_files=None,
- without_egg_link=False,
- use_user_site=False,
- sub_dir=False,
- ):
+ pkg_name: str,
+ editable: bool = True,
+ with_files: Optional[List[str]] = None,
+ without_files: Optional[List[str]] = None,
+ without_egg_link: bool = False,
+ use_user_site: bool = False,
+ sub_dir: Optional[str] = None,
+ ) -> None:
with_files = with_files or []
without_files = without_files or []
e = self.test_env
@@ -282,9 +327,9 @@ class TestPipResult:
pkg_dir = e.site_packages / pkg_name
if use_user_site:
- egg_link_path = e.user_site / pkg_name + ".egg-link"
+ egg_link_path = e.user_site / f"{pkg_name}.egg-link"
else:
- egg_link_path = e.site_packages / pkg_name + ".egg-link"
+ egg_link_path = e.site_packages / f"{pkg_name}.egg-link"
if without_egg_link:
if egg_link_path in self.files_created:
@@ -303,9 +348,9 @@ class TestPipResult:
# FIXME: I don't understand why there's a trailing . here
if not (
egg_link_contents.endswith("\n.")
- and egg_link_contents[:-2].endswith(pkg_dir)
+ and egg_link_contents[:-2].endswith(os.fspath(pkg_dir))
):
- expected_ending = pkg_dir + "\n."
+ expected_ending = f"{pkg_dir}\n."
raise TestFailure(
textwrap.dedent(
f"""
@@ -327,9 +372,9 @@ class TestPipResult:
maybe = "" if without_egg_link else "not "
raise TestFailure(f"{pth_file} unexpectedly {maybe}updated by install")
- if (pkg_dir in self.files_created) == (curdir in without_files):
- maybe = "not " if curdir in without_files else ""
- files = sorted(self.files_created)
+ if (pkg_dir in self.files_created) == (os.curdir in without_files):
+ maybe = "not " if os.curdir in without_files else ""
+ files = sorted(p.as_posix() for p in self.files_created)
raise TestFailure(
textwrap.dedent(
f"""
@@ -354,20 +399,20 @@ class TestPipResult:
f"Package directory {pkg_dir!r} has unexpected content {f}"
)
- def did_create(self, path, message=None):
- assert str(path) in self.files_created, _one_or_both(message, self)
+ def did_create(self, path: StrPath, message: Optional[str] = None) -> None:
+ assert path in self.files_created, _one_or_both(message, self)
- def did_not_create(self, path, message=None):
- assert str(path) not in self.files_created, _one_or_both(message, self)
+ def did_not_create(self, p: StrPath, message: Optional[str] = None) -> None:
+ assert p not in self.files_created, _one_or_both(message, self)
- def did_update(self, path, message=None):
- assert str(path) in self.files_updated, _one_or_both(message, self)
+ def did_update(self, path: StrPath, message: Optional[str] = None) -> None:
+ assert path in self.files_updated, _one_or_both(message, self)
- def did_not_update(self, path, message=None):
- assert str(path) not in self.files_updated, _one_or_both(message, self)
+ def did_not_update(self, p: StrPath, message: Optional[str] = None) -> None:
+ assert p not in self.files_updated, _one_or_both(message, self)
-def _one_or_both(a, b):
+def _one_or_both(a: Optional[str], b: Any) -> str:
"""Returns f"{a}\n{b}" if a is truthy, else returns str(b)."""
if not a:
return str(b)
@@ -375,7 +420,7 @@ def _one_or_both(a, b):
return f"{a}\n{b}"
-def make_check_stderr_message(stderr, line, reason):
+def make_check_stderr_message(stderr: str, line: str, reason: str) -> str:
"""
Create an exception message to use inside check_stderr().
"""
@@ -389,10 +434,10 @@ def make_check_stderr_message(stderr, line, reason):
def _check_stderr(
- stderr,
- allow_stderr_warning,
- allow_stderr_error,
-):
+ stderr: str,
+ allow_stderr_warning: bool,
+ allow_stderr_error: bool,
+) -> None:
"""
Check the given stderr for logged warnings and errors.
@@ -405,6 +450,7 @@ def _check_stderr(
lines = stderr.splitlines()
for line in lines:
+ line = line.lstrip()
# First check for logging errors, which we don't allow during
# tests even if allow_stderr_error=True (since a logging error
# would signal a bug in pip's code).
@@ -431,7 +477,7 @@ def _check_stderr(
if allow_stderr_warning:
continue
- if line.startswith("WARNING: ") or line.startswith(DEPRECATION_MSG_PREFIX):
+ if line.startswith("WARNING: "):
reason = (
"stderr has an unexpected warning "
"(pass allow_stderr_warning=True to permit this)"
@@ -457,28 +503,31 @@ class PipTestEnvironment(TestFileEnvironment):
exe = sys.platform == "win32" and ".exe" or ""
verbose = False
- def __init__(self, base_path, *args, virtualenv, pip_expect_warning=None, **kwargs):
- # Make our base_path a test.lib.path.Path object
- base_path = Path(base_path)
-
+ def __init__(
+ self,
+ base_path: pathlib.Path,
+ *args: Any,
+ virtualenv: VirtualEnvironment,
+ pip_expect_warning: bool = False,
+ zipapp: Optional[str] = None,
+ **kwargs: Any,
+ ) -> None:
# Store paths related to the virtual environment
self.venv_path = virtualenv.location
self.lib_path = virtualenv.lib
self.site_packages_path = virtualenv.site
self.bin_path = virtualenv.bin
+ assert site.USER_BASE is not None
+ assert site.USER_SITE is not None
+
self.user_base_path = self.venv_path.joinpath("user")
self.user_site_path = self.venv_path.joinpath(
"user",
site.USER_SITE[len(site.USER_BASE) + 1 :],
)
if sys.platform == "win32":
- if sys.version_info >= (3, 5):
- scripts_base = Path(
- os.path.normpath(self.user_site_path.joinpath(".."))
- )
- else:
- scripts_base = self.user_base_path
+ scripts_base = self.user_site_path.joinpath("..").resolve()
self.user_bin_path = scripts_base.joinpath("Scripts")
else:
self.user_bin_path = self.user_base_path.joinpath(
@@ -494,8 +543,8 @@ class PipTestEnvironment(TestFileEnvironment):
# Setup our environment
environ = kwargs.setdefault("environ", os.environ.copy())
- environ["PATH"] = Path.pathsep.join(
- [self.bin_path] + [environ.get("PATH", [])],
+ environ["PATH"] = os.pathsep.join(
+ [os.fspath(self.bin_path), environ.get("PATH", "")],
)
environ["PYTHONUSERBASE"] = self.user_base_path
# Writing bytecode can mess up updated file detection
@@ -507,6 +556,9 @@ class PipTestEnvironment(TestFileEnvironment):
# (useful for Python version deprecation)
self.pip_expect_warning = pip_expect_warning
+ # The name of an (optional) zipapp to use when running pip
+ self.zipapp = zipapp
+
# Call the TestFileEnvironment __init__
super().__init__(base_path, *args, **kwargs)
@@ -523,13 +575,13 @@ class PipTestEnvironment(TestFileEnvironment):
"scratch",
]:
real_name = f"{name}_path"
- relative_path = Path(
+ relative_path = pathlib.Path(
os.path.relpath(getattr(self, real_name), self.base_path)
)
setattr(self, name, relative_path)
# Make sure temp_path is a Path object
- self.temp_path = Path(self.temp_path)
+ self.temp_path: pathlib.Path = pathlib.Path(self.temp_path)
# Ensure the tmp dir exists, things break horribly if it doesn't
self.temp_path.mkdir()
@@ -538,14 +590,18 @@ class PipTestEnvironment(TestFileEnvironment):
self.user_site_path.mkdir(parents=True)
self.user_site_path.joinpath("easy-install.pth").touch()
- def _ignore_file(self, fn):
+ def _ignore_file(self, fn: str) -> bool:
if fn.endswith("__pycache__") or fn.endswith(".pyc"):
result = True
+ elif self.zipapp and fn.endswith("cacert.pem"):
+ # Temporary copies of cacert.pem are extracted
+ # when running from a zipapp
+ result = True
else:
result = super()._ignore_file(fn)
return result
- def _find_traverse(self, path, result):
+ def _find_traverse(self, path: str, result: Dict[str, FoundDir]) -> None:
# Ignore symlinked directories to avoid duplicates in `run()`
# results because of venv `lib64 -> lib/` symlink on Linux.
full = os.path.join(self.base_path, path)
@@ -557,14 +613,13 @@ class PipTestEnvironment(TestFileEnvironment):
def run(
self,
- *args,
- cwd=None,
- run_from=None,
- allow_stderr_error=None,
- allow_stderr_warning=None,
- allow_error=None,
- **kw,
- ):
+ *args: str,
+ cwd: Optional[StrPath] = None,
+ allow_stderr_error: Optional[bool] = None,
+ allow_stderr_warning: Optional[bool] = None,
+ allow_error: bool = False,
+ **kw: Any,
+ ) -> TestPipResult:
"""
:param allow_stderr_error: whether a logged error is allowed in
stderr. Passing True for this argument implies
@@ -587,11 +642,10 @@ class PipTestEnvironment(TestFileEnvironment):
if self.verbose:
print(f">> running {args} {kw}")
- assert not cwd or not run_from, "Don't use run_from; it's going away"
- cwd = cwd or run_from or self.cwd
+ cwd = cwd or self.cwd
if sys.platform == "win32":
# Partial fix for ScriptTest.run using `shell=True` on Windows.
- args = [str(a).replace("^", "^^").replace("&", "^&") for a in args]
+ args = tuple(str(a).replace("^", "^^").replace("&", "^&") for a in args)
if allow_error:
kw["expect_error"] = True
@@ -635,7 +689,7 @@ class PipTestEnvironment(TestFileEnvironment):
if expect_error and not allow_error:
if result.returncode == 0:
__tracebackhide__ = True
- raise AssertionError("Script passed unexpectedly.")
+ raise AssertionError(f"Script passed unexpectedly:\n{result}")
_check_stderr(
result.stderr,
@@ -645,32 +699,44 @@ class PipTestEnvironment(TestFileEnvironment):
return TestPipResult(result, verbose=self.verbose)
- def pip(self, *args, use_module=True, **kwargs):
+ def pip(
+ self,
+ *args: StrPath,
+ use_module: bool = True,
+ **kwargs: Any,
+ ) -> TestPipResult:
__tracebackhide__ = True
if self.pip_expect_warning:
kwargs["allow_stderr_warning"] = True
- if use_module:
+ if self.zipapp:
+ exe = "python"
+ args = (self.zipapp,) + args
+ elif use_module:
exe = "python"
args = ("-m", "pip") + args
else:
exe = "pip"
- return self.run(exe, *args, **kwargs)
+ return self.run(exe, *(os.fspath(a) for a in args), **kwargs)
- def pip_install_local(self, *args, **kwargs):
+ def pip_install_local(
+ self,
+ *args: StrPath,
+ **kwargs: Any,
+ ) -> TestPipResult:
return self.pip(
"install",
"--no-index",
"--find-links",
- path_to_url(os.path.join(DATA_DIR, "packages")),
+ pathlib.Path(DATA_DIR, "packages").as_uri(),
*args,
**kwargs,
)
- def easy_install(self, *args, **kwargs):
+ def easy_install(self, *args: str, **kwargs: Any) -> TestPipResult:
args = ("-m", "easy_install") + args
return self.run("python", *args, **kwargs)
- def assert_installed(self, **kwargs):
+ def assert_installed(self, **kwargs: str) -> None:
ret = self.pip("list", "--format=json")
installed = set(
(canonicalize_name(val["name"]), val["version"])
@@ -679,7 +745,7 @@ class PipTestEnvironment(TestFileEnvironment):
expected = set((canonicalize_name(k), v) for k, v in kwargs.items())
assert expected <= installed, "{!r} not all in {!r}".format(expected, installed)
- def assert_not_installed(self, *args):
+ def assert_not_installed(self, *args: str) -> None:
ret = self.pip("list", "--format=json")
installed = set(
canonicalize_name(val["name"]) for val in json.loads(ret.stdout)
@@ -695,7 +761,9 @@ class PipTestEnvironment(TestFileEnvironment):
# FIXME ScriptTest does something similar, but only within a single
# ProcResult; this generalizes it so states can be compared across
# multiple commands. Maybe should be rolled into ScriptTest?
-def diff_states(start, end, ignore=None):
+def diff_states(
+ start: _FilesState, end: _FilesState, ignore: Iterable[StrPath] = ()
+) -> Dict[str, _FilesState]:
"""
Differences two "filesystem states" as represented by dictionaries
of FoundFile and FoundDir objects.
@@ -719,9 +787,9 @@ def diff_states(start, end, ignore=None):
size are considered.
"""
- ignore = ignore or []
- def prefix_match(path, prefix):
+ def prefix_match(path: str, prefix_path: StrPath) -> bool:
+ prefix = os.fspath(prefix_path)
if path == prefix:
return True
prefix = prefix.rstrip(os.path.sep) + os.path.sep
@@ -740,7 +808,11 @@ def diff_states(start, end, ignore=None):
return dict(deleted=deleted, created=created, updated=updated)
-def assert_all_changes(start_state, end_state, expected_changes):
+def assert_all_changes(
+ start_state: Union[_FilesState, TestPipResult],
+ end_state: Union[_FilesState, TestPipResult],
+ expected_changes: List[StrPath],
+) -> Dict[str, _FilesState]:
"""
Fails if anything changed that isn't listed in the
expected_changes.
@@ -761,6 +833,8 @@ def assert_all_changes(start_state, end_state, expected_changes):
start_files = start_state.files_before
if isinstance(end_state, TestPipResult):
end_files = end_state.files_after
+ start_files = cast(_FilesState, start_files)
+ end_files = cast(_FilesState, end_files)
diff = diff_states(start_files, end_files, ignore=expected_changes)
if list(diff.values()) != [{}, {}, {}]:
@@ -773,7 +847,11 @@ def assert_all_changes(start_state, end_state, expected_changes):
return diff
-def _create_main_file(dir_path, name=None, output=None):
+def _create_main_file(
+ dir_path: pathlib.Path,
+ name: Optional[str] = None,
+ output: Optional[str] = None,
+) -> None:
"""
Create a module with a main() function that prints the given output.
"""
@@ -792,12 +870,12 @@ def _create_main_file(dir_path, name=None, output=None):
def _git_commit(
- env_or_script,
- repo_dir,
- message=None,
- allow_empty=False,
- stage_modified=False,
-):
+ env_or_script: PipTestEnvironment,
+ repo_dir: StrPath,
+ message: Optional[str] = None,
+ allow_empty: bool = False,
+ stage_modified: bool = False,
+) -> None:
"""
Run git-commit.
@@ -829,57 +907,67 @@ def _git_commit(
env_or_script.run(*new_args, cwd=repo_dir)
-def _vcs_add(script, version_pkg_path, vcs="git"):
+def _vcs_add(
+ location: pathlib.Path,
+ version_pkg_path: pathlib.Path,
+ vcs: str = "git",
+) -> pathlib.Path:
if vcs == "git":
- script.run("git", "init", cwd=version_pkg_path)
- script.run("git", "add", ".", cwd=version_pkg_path)
- _git_commit(script, version_pkg_path, message="initial version")
+ subprocess.check_call(["git", "init"], cwd=os.fspath(version_pkg_path))
+ subprocess.check_call(["git", "add", "."], cwd=os.fspath(version_pkg_path))
+ subprocess.check_call(
+ ["git", "commit", "-m", "initial version"], cwd=os.fspath(version_pkg_path)
+ )
elif vcs == "hg":
- script.run("hg", "init", cwd=version_pkg_path)
- script.run("hg", "add", ".", cwd=version_pkg_path)
- script.run(
- "hg",
- "commit",
- "-q",
- "--user",
- "pip <distutils-sig@python.org>",
- "-m",
- "initial version",
- cwd=version_pkg_path,
+ subprocess.check_call(["hg", "init"], cwd=os.fspath(version_pkg_path))
+ subprocess.check_call(["hg", "add", "."], cwd=os.fspath(version_pkg_path))
+ subprocess.check_call(
+ [
+ "hg",
+ "commit",
+ "-q",
+ "--user",
+ "pip <distutils-sig@python.org>",
+ "-m",
+ "initial version",
+ ],
+ cwd=os.fspath(version_pkg_path),
)
elif vcs == "svn":
- repo_url = _create_svn_repo(script, version_pkg_path)
- script.run(
- "svn", "checkout", repo_url, "pip-test-package", cwd=script.scratch_path
+ repo_url = _create_svn_repo(location, version_pkg_path)
+ subprocess.check_call(
+ ["svn", "checkout", repo_url, "pip-test-package"], cwd=os.fspath(location)
)
- checkout_path = script.scratch_path / "pip-test-package"
-
- # svn internally stores windows drives as uppercase; we'll match that.
- checkout_path = checkout_path.replace("c:", "C:")
+ checkout_path = location / "pip-test-package"
version_pkg_path = checkout_path
elif vcs == "bazaar":
- script.run("bzr", "init", cwd=version_pkg_path)
- script.run("bzr", "add", ".", cwd=version_pkg_path)
- script.run(
- "bzr", "whoami", "pip <distutils-sig@python.org>", cwd=version_pkg_path
+ subprocess.check_call(["bzr", "init"], cwd=os.fspath(version_pkg_path))
+ subprocess.check_call(["bzr", "add", "."], cwd=os.fspath(version_pkg_path))
+ subprocess.check_call(
+ ["bzr", "whoami", "pip <distutils-sig@python.org>"],
+ cwd=os.fspath(version_pkg_path),
)
- script.run(
- "bzr",
- "commit",
- "-q",
- "--author",
- "pip <distutils-sig@python.org>",
- "-m",
- "initial version",
- cwd=version_pkg_path,
+ subprocess.check_call(
+ [
+ "bzr",
+ "commit",
+ "-q",
+ "--author",
+ "pip <distutils-sig@python.org>",
+ "-m",
+ "initial version",
+ ],
+ cwd=os.fspath(version_pkg_path),
)
else:
raise ValueError(f"Unknown vcs: {vcs}")
return version_pkg_path
-def _create_test_package_with_subdirectory(script, subdirectory):
+def _create_test_package_with_subdirectory(
+ script: PipTestEnvironment, subdirectory: str
+) -> pathlib.Path:
script.scratch_path.joinpath("version_pkg").mkdir()
version_pkg_path = script.scratch_path / "version_pkg"
_create_main_file(version_pkg_path, name="version_pkg", output="0.1")
@@ -926,9 +1014,11 @@ def _create_test_package_with_subdirectory(script, subdirectory):
return version_pkg_path
-def _create_test_package_with_srcdir(script, name="version_pkg", vcs="git"):
- script.scratch_path.joinpath(name).mkdir()
- version_pkg_path = script.scratch_path / name
+def _create_test_package_with_srcdir(
+ dir_path: pathlib.Path, name: str = "version_pkg", vcs: str = "git"
+) -> pathlib.Path:
+ dir_path.joinpath(name).mkdir()
+ version_pkg_path = dir_path / name
subdir_path = version_pkg_path.joinpath("subdir")
subdir_path.mkdir()
src_path = subdir_path.joinpath("src")
@@ -951,12 +1041,14 @@ def _create_test_package_with_srcdir(script, name="version_pkg", vcs="git"):
)
)
)
- return _vcs_add(script, version_pkg_path, vcs)
+ return _vcs_add(dir_path, version_pkg_path, vcs)
-def _create_test_package(script, name="version_pkg", vcs="git"):
- script.scratch_path.joinpath(name).mkdir()
- version_pkg_path = script.scratch_path / name
+def _create_test_package(
+ dir_path: pathlib.Path, name: str = "version_pkg", vcs: str = "git"
+) -> pathlib.Path:
+ dir_path.joinpath(name).mkdir()
+ version_pkg_path = dir_path / name
_create_main_file(version_pkg_path, name=name, output="0.1")
version_pkg_path.joinpath("setup.py").write_text(
textwrap.dedent(
@@ -974,25 +1066,31 @@ def _create_test_package(script, name="version_pkg", vcs="git"):
)
)
)
- return _vcs_add(script, version_pkg_path, vcs)
-
-
-def _create_svn_repo(script, version_pkg_path):
- repo_url = path_to_url(script.scratch_path / "pip-test-package-repo" / "trunk")
- script.run("svnadmin", "create", "pip-test-package-repo", cwd=script.scratch_path)
- script.run(
- "svn",
- "import",
- version_pkg_path,
- repo_url,
- "-m",
- "Initial import of pip-test-package",
- cwd=script.scratch_path,
+ return _vcs_add(dir_path, version_pkg_path, vcs)
+
+
+def _create_svn_repo(repo_path: pathlib.Path, version_pkg_path: StrPath) -> str:
+ repo_url = repo_path.joinpath("pip-test-package-repo", "trunk").as_uri()
+ subprocess.check_call(
+ "svnadmin create pip-test-package-repo".split(), cwd=repo_path
+ )
+ subprocess.check_call(
+ [
+ "svn",
+ "import",
+ os.fspath(version_pkg_path),
+ repo_url,
+ "-m",
+ "Initial import of pip-test-package",
+ ],
+ cwd=os.fspath(repo_path),
)
return repo_url
-def _change_test_package_version(script, version_pkg_path):
+def _change_test_package_version(
+ script: PipTestEnvironment, version_pkg_path: pathlib.Path
+) -> None:
_create_main_file(
version_pkg_path, name="version_pkg", output="some different version"
)
@@ -1000,21 +1098,8 @@ def _change_test_package_version(script, version_pkg_path):
_git_commit(script, version_pkg_path, message="messed version", stage_modified=True)
-def assert_raises_regexp(exception, reg, run, *args, **kwargs):
- """Like assertRaisesRegexp in unittest"""
- __tracebackhide__ = True
-
- try:
- run(*args, **kwargs)
- assert False, f"{exception} should have been thrown"
- except exception:
- e = sys.exc_info()[1]
- p = re.compile(reg)
- assert p.search(str(e)), str(e)
-
-
@contextmanager
-def requirements_file(contents, tmpdir):
+def requirements_file(contents: str, tmpdir: pathlib.Path) -> Iterator[pathlib.Path]:
"""Return a Path to a requirements file of given contents.
As long as the context manager is open, the requirements file will exist.
@@ -1028,7 +1113,9 @@ def requirements_file(contents, tmpdir):
path.unlink()
-def create_test_package_with_setup(script, **setup_kwargs):
+def create_test_package_with_setup(
+ script: PipTestEnvironment, **setup_kwargs: Any
+) -> pathlib.Path:
assert "name" in setup_kwargs, setup_kwargs
pkg_path = script.scratch_path / setup_kwargs["name"]
pkg_path.mkdir()
@@ -1044,17 +1131,15 @@ def create_test_package_with_setup(script, **setup_kwargs):
return pkg_path
-def urlsafe_b64encode_nopad(data):
- # type: (bytes) -> str
+def urlsafe_b64encode_nopad(data: bytes) -> str:
return urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
-def create_really_basic_wheel(name, version):
- # type: (str, str) -> bytes
- def digest(contents):
+def create_really_basic_wheel(name: str, version: str) -> bytes:
+ def digest(contents: bytes) -> str:
return "sha256={}".format(urlsafe_b64encode_nopad(sha256(contents).digest()))
- def add_file(path, text):
+ def add_file(path: str, text: str) -> None:
contents = text.encode("utf-8")
z.writestr(path, contents)
records.append((path, digest(contents), str(len(contents))))
@@ -1083,14 +1168,14 @@ def create_really_basic_wheel(name, version):
def create_basic_wheel_for_package(
- script,
- name,
- version,
- depends=None,
- extras=None,
- requires_python=None,
- extra_files=None,
-):
+ script: PipTestEnvironment,
+ name: str,
+ version: str,
+ depends: Optional[List[str]] = None,
+ extras: Optional[Dict[str, List[str]]] = None,
+ requires_python: Optional[str] = None,
+ extra_files: Optional[Dict[str, Union[bytes, str]]] = None,
+) -> pathlib.Path:
if depends is None:
depends = []
if extras is None:
@@ -1120,7 +1205,7 @@ def create_basic_wheel_for_package(
for package in packages
]
- metadata_updates = {
+ metadata_updates: Dict[str, Any] = {
"Provides-Extra": list(extras),
"Requires-Dist": requires_dist,
}
@@ -1142,22 +1227,49 @@ def create_basic_wheel_for_package(
return archive_path
-def create_basic_sdist_for_package(script, name, version, extra_files=None):
+def create_basic_sdist_for_package(
+ script: PipTestEnvironment,
+ name: str,
+ version: str,
+ extra_files: Optional[Dict[str, str]] = None,
+ *,
+ fails_egg_info: bool = False,
+ fails_bdist_wheel: bool = False,
+ depends: Optional[List[str]] = None,
+ setup_py_prelude: str = "",
+) -> pathlib.Path:
files = {
- "setup.py": """
+ "setup.py": textwrap.dedent(
+ """\
+ import sys
from setuptools import find_packages, setup
- setup(name={name!r}, version={version!r})
- """,
+
+ {setup_py_prelude}
+
+ fails_bdist_wheel = {fails_bdist_wheel!r}
+ fails_egg_info = {fails_egg_info!r}
+
+ if fails_egg_info and "egg_info" in sys.argv:
+ raise Exception("Simulated failure for generating metadata.")
+
+ if fails_bdist_wheel and "bdist_wheel" in sys.argv:
+ raise Exception("Simulated failure for building a wheel.")
+
+ setup(name={name!r}, version={version!r},
+ install_requires={depends!r})
+ """
+ ).format(
+ name=name,
+ version=version,
+ depends=depends or [],
+ setup_py_prelude=setup_py_prelude,
+ fails_bdist_wheel=fails_bdist_wheel,
+ fails_egg_info=fails_egg_info,
+ ),
}
# Some useful shorthands
- archive_name = "{name}-{version}.tar.gz".format(name=name, version=version)
-
- # Replace key-values with formatted values
- for key, value in list(files.items()):
- del files[key]
- key = key.format(name=name)
- files[key] = textwrap.dedent(value).format(name=name, version=version).strip()
+ archive_name = f"{name}-{version}.tar.gz"
# Add new files after formatting
if extra_files:
@@ -1170,7 +1282,7 @@ def create_basic_sdist_for_package(script, name, version, extra_files=None):
retval = script.scratch_path / archive_name
generated = shutil.make_archive(
- retval,
+ os.fspath(retval),
"gztar",
root_dir=script.temp_path,
base_dir=os.curdir,
@@ -1183,8 +1295,8 @@ def create_basic_sdist_for_package(script, name, version, extra_files=None):
return retval
-def need_executable(name, check_cmd):
- def wrapper(fn):
+def need_executable(name: str, check_cmd: Tuple[str, ...]) -> Callable[[_Test], _Test]:
+ def wrapper(fn: _Test) -> _Test:
try:
subprocess.check_output(check_cmd)
except (OSError, subprocess.CalledProcessError):
@@ -1194,7 +1306,7 @@ def need_executable(name, check_cmd):
return wrapper
-def is_bzr_installed():
+def is_bzr_installed() -> bool:
try:
subprocess.check_output(("bzr", "version", "--short"))
except OSError:
@@ -1202,7 +1314,7 @@ def is_bzr_installed():
return True
-def is_svn_installed():
+def is_svn_installed() -> bool:
try:
subprocess.check_output(("svn", "--version"))
except OSError:
@@ -1210,11 +1322,11 @@ def is_svn_installed():
return True
-def need_bzr(fn):
+def need_bzr(fn: _Test) -> _Test:
return pytest.mark.bzr(need_executable("Bazaar", ("bzr", "version", "--short"))(fn))
-def need_svn(fn):
+def need_svn(fn: _Test) -> _Test:
return pytest.mark.svn(
need_executable("Subversion", ("svn", "--version"))(
need_executable("Subversion Admin", ("svnadmin", "--version"))(fn)
@@ -1222,5 +1334,5 @@ def need_svn(fn):
)
-def need_mercurial(fn):
+def need_mercurial(fn: _Test) -> _Test:
return pytest.mark.mercurial(need_executable("Mercurial", ("hg", "version"))(fn))