Skip to content

Commit

Permalink
Merge pull request #11170 from uranusjr/pathlib
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr authored Jun 12, 2022
2 parents 0a4941a + ad1b2d3 commit ba11a20
Show file tree
Hide file tree
Showing 74 changed files with 564 additions and 734 deletions.
95 changes: 60 additions & 35 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@
import sys
import time
from contextlib import ExitStack, contextmanager
from typing import TYPE_CHECKING, Callable, Dict, Iterable, Iterator, List, Optional
from pathlib import Path
from typing import (
TYPE_CHECKING,
Callable,
Dict,
Iterable,
Iterator,
List,
Optional,
Union,
)
from unittest.mock import patch

import py.path
import pytest

# Config will be available from the public API in pytest >= 7.0.0:
Expand All @@ -27,7 +36,6 @@
from pip._internal.locations import _USE_SYSCONFIG
from pip._internal.utils.temp_dir import global_tempdir_manager
from tests.lib import DATA_DIR, SRC_DIR, PipTestEnvironment, TestData
from tests.lib.path import Path
from tests.lib.server import MockServer as _MockServer
from tests.lib.server import make_mock_server, server_running
from tests.lib.venv import VirtualEnvironment, VirtualEnvironmentType
Expand Down Expand Up @@ -146,38 +154,55 @@ def resolver_variant(request: pytest.FixtureRequest) -> Iterator[str]:


@pytest.fixture(scope="session")
def tmpdir_factory(
request: pytest.FixtureRequest, tmpdir_factory: pytest.TempdirFactory
) -> Iterator[pytest.TempdirFactory]:
def tmp_path_factory(
request: pytest.FixtureRequest, tmp_path_factory: pytest.TempPathFactory
) -> Iterator[pytest.TempPathFactory]:
"""Modified `tmpdir_factory` session fixture
that will automatically cleanup after itself.
"""
yield tmpdir_factory
yield tmp_path_factory
if not request.config.getoption("--keep-tmpdir"):
shutil.rmtree(
tmpdir_factory.getbasetemp(),
tmp_path_factory.getbasetemp(),
ignore_errors=True,
)


@pytest.fixture(scope="session")
def tmpdir_factory(tmp_path_factory: pytest.TempPathFactory) -> pytest.TempPathFactory:
"""Override Pytest's ``tmpdir_factory`` with our pathlib implementation.
This prevents mis-use of this fixture.
"""
return tmp_path_factory


@pytest.fixture
def tmpdir(request: pytest.FixtureRequest, tmpdir: py.path.local) -> Iterator[Path]:
def tmp_path(request: pytest.FixtureRequest, tmp_path: Path) -> Iterator[Path]:
"""
Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory. The returned object is a ``tests.lib.path.Path`` object.
directory. The returned object is a ``Path`` object.
This uses the built-in tmpdir fixture from pytest itself but modified
to return our typical path object instead of py.path.local as well as
deleting the temporary directories at the end of each test case.
This uses the built-in tmp_path fixture from pytest itself, but deletes the
temporary directories at the end of each test case.
"""
assert tmpdir.isdir()
yield Path(str(tmpdir))
assert tmp_path.is_dir()
yield tmp_path
# Clear out the temporary directory after the test has finished using it.
# This should prevent us from needing a multiple gigabyte temporary
# directory while running the tests.
if not request.config.getoption("--keep-tmpdir"):
tmpdir.remove(ignore_errors=True)
shutil.rmtree(tmp_path, ignore_errors=True)


@pytest.fixture()
def tmpdir(tmp_path: Path) -> Path:
"""Override Pytest's ``tmpdir`` with our pathlib implementation.
This prevents mis-use of this fixture.
"""
return tmp_path


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -296,10 +321,10 @@ def scoped_global_tempdir_manager(request: pytest.FixtureRequest) -> Iterator[No


@pytest.fixture(scope="session")
def pip_src(tmpdir_factory: pytest.TempdirFactory) -> Path:
def pip_src(tmpdir_factory: pytest.TempPathFactory) -> Path:
def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]:
# In the root directory...
if path == SRC_DIR:
if os.path.samefile(path, SRC_DIR):
# ignore all folders except "src"
folders = {
name for name in names if os.path.isdir(os.path.join(path, name))
Expand All @@ -317,7 +342,7 @@ def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]:
ignored.update(fnmatch.filter(names, pattern))
return ignored

pip_src = Path(str(tmpdir_factory.mktemp("pip_src"))).joinpath("pip_src")
pip_src = tmpdir_factory.mktemp("pip_src").joinpath("pip_src")
# Copy over our source tree so that each use is self contained
shutil.copytree(
SRC_DIR,
Expand All @@ -328,11 +353,11 @@ def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]:


def _common_wheel_editable_install(
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path, package: str
tmpdir_factory: pytest.TempPathFactory, common_wheels: Path, package: str
) -> Path:
wheel_candidates = list(common_wheels.glob(f"{package}-*.whl"))
assert len(wheel_candidates) == 1, wheel_candidates
install_dir = Path(str(tmpdir_factory.mktemp(package))) / "install"
install_dir = tmpdir_factory.mktemp(package) / "install"
Wheel(wheel_candidates[0]).install_as_egg(install_dir)
(install_dir / "EGG-INFO").rename(install_dir / f"{package}.egg-info")
assert compileall.compile_dir(str(install_dir), quiet=1)
Expand All @@ -341,19 +366,19 @@ def _common_wheel_editable_install(

@pytest.fixture(scope="session")
def setuptools_install(
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path
tmpdir_factory: pytest.TempPathFactory, common_wheels: Path
) -> Path:
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "setuptools")


@pytest.fixture(scope="session")
def wheel_install(tmpdir_factory: pytest.TempdirFactory, common_wheels: Path) -> Path:
def wheel_install(tmpdir_factory: pytest.TempPathFactory, common_wheels: Path) -> Path:
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "wheel")


@pytest.fixture(scope="session")
def coverage_install(
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path
tmpdir_factory: pytest.TempPathFactory, common_wheels: Path
) -> Path:
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "coverage")

Expand All @@ -370,7 +395,7 @@ def install_egg_link(
@pytest.fixture(scope="session")
def virtualenv_template(
request: pytest.FixtureRequest,
tmpdir_factory: pytest.TempdirFactory,
tmpdir_factory: pytest.TempPathFactory,
pip_src: Path,
setuptools_install: Path,
coverage_install: Path,
Expand All @@ -383,12 +408,12 @@ def virtualenv_template(
venv_type = "virtualenv"

# Create the virtual environment
tmpdir = Path(str(tmpdir_factory.mktemp("virtualenv")))
tmpdir = tmpdir_factory.mktemp("virtualenv")
venv = VirtualEnvironment(tmpdir.joinpath("venv_orig"), venv_type=venv_type)

# Install setuptools and pip.
install_egg_link(venv, "setuptools", setuptools_install)
pip_editable = Path(str(tmpdir_factory.mktemp("pip"))) / "pip"
pip_editable = tmpdir_factory.mktemp("pip") / "pip"
shutil.copytree(pip_src, pip_editable, symlinks=True)
# noxfile.py is Python 3 only
assert compileall.compile_dir(
Expand All @@ -397,7 +422,7 @@ def virtualenv_template(
rx=re.compile("noxfile.py$"),
)
subprocess.check_call(
[venv.bin / "python", "setup.py", "-q", "develop"], cwd=pip_editable
[os.fspath(venv.bin / "python"), "setup.py", "-q", "develop"], cwd=pip_editable
)

# Install coverage and pth file for executing it in any spawned processes
Expand Down Expand Up @@ -493,7 +518,7 @@ def factory(
def script(
tmpdir: Path,
virtualenv: VirtualEnvironment,
script_factory: Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment],
script_factory: ScriptFactory,
) -> PipTestEnvironment:
"""
Return a PipTestEnvironment which is unique to each test function and
Expand All @@ -511,8 +536,8 @@ def common_wheels() -> Path:


@pytest.fixture(scope="session")
def shared_data(tmpdir_factory: pytest.TempdirFactory) -> TestData:
return TestData.copy(Path(str(tmpdir_factory.mktemp("data"))))
def shared_data(tmpdir_factory: pytest.TempPathFactory) -> TestData:
return TestData.copy(tmpdir_factory.mktemp("data"))


@pytest.fixture
Expand All @@ -527,12 +552,12 @@ def __init__(self, returncode: int, stdout: str) -> None:


class InMemoryPip:
def pip(self, *args: str) -> InMemoryPipResult:
def pip(self, *args: Union[str, Path]) -> InMemoryPipResult:
orig_stdout = sys.stdout
stdout = io.StringIO()
sys.stdout = stdout
try:
returncode = pip_entry_point(list(args))
returncode = pip_entry_point([os.fspath(a) for a in args])
except SystemExit as e:
returncode = e.code or 0
finally:
Expand All @@ -555,15 +580,15 @@ def deprecated_python() -> bool:


@pytest.fixture(scope="session")
def cert_factory(tmpdir_factory: pytest.TempdirFactory) -> CertFactory:
def cert_factory(tmpdir_factory: pytest.TempPathFactory) -> CertFactory:
# Delay the import requiring cryptography in order to make it possible
# to deselect relevant tests on systems where cryptography cannot
# be installed.
from tests.lib.certs import make_tls_cert, serialize_cert, serialize_key

def factory() -> str:
"""Returns path to cert/key file."""
output_path = Path(str(tmpdir_factory.mktemp("certs"))) / "cert.pem"
output_path = tmpdir_factory.mktemp("certs") / "cert.pem"
# Must be Text on PY2.
cert, key = make_tls_cert("localhost")
with open(str(output_path), "wb") as f:
Expand Down
3 changes: 1 addition & 2 deletions tests/functional/test_broken_stdout.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import os
import subprocess
from pathlib import Path
from typing import List, Tuple

from tests.lib.path import Path

_BROKEN_STDOUT_RETURN_CODE = 120


Expand Down
7 changes: 4 additions & 3 deletions tests/functional/test_build_env.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from textwrap import dedent
from typing import Optional

Expand Down Expand Up @@ -69,11 +70,11 @@ def run_with_build_env(
" ",
)
)
args = ["python", build_env_script]
args = ["python", os.fspath(build_env_script)]
if test_script_contents is not None:
test_script = script.scratch_path / "test.py"
test_script.write_text(dedent(test_script_contents))
args.append(test_script)
args.append(os.fspath(test_script))
return script.run(*args)


Expand All @@ -89,7 +90,7 @@ def test_build_env_allow_empty_requirements_install() -> None:
def test_build_env_allow_only_one_install(script: PipTestEnvironment) -> None:
create_basic_wheel_for_package(script, "foo", "1.0")
create_basic_wheel_for_package(script, "bar", "1.0")
finder = make_test_finder(find_links=[script.scratch_path])
finder = make_test_finder(find_links=[os.fspath(script.scratch_path)])
build_env = BuildEnvironment()
for prefix in ("normal", "overlay"):
build_env.install_requirements(
Expand Down
16 changes: 8 additions & 8 deletions tests/functional/test_completion.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import os
import sys
from typing import TYPE_CHECKING, Optional, Tuple
from pathlib import Path
from typing import TYPE_CHECKING, Tuple, Union

import pytest

from tests.conftest import ScriptFactory
from tests.lib import PipTestEnvironment, TestData, TestPipResult
from tests.lib.path import Path

if TYPE_CHECKING:
from typing import Protocol
Expand Down Expand Up @@ -83,12 +83,12 @@

@pytest.fixture(scope="session")
def script_with_launchers(
tmpdir_factory: pytest.TempdirFactory,
tmpdir_factory: pytest.TempPathFactory,
script_factory: ScriptFactory,
common_wheels: Path,
pip_src: Path,
) -> PipTestEnvironment:
tmpdir = Path(str(tmpdir_factory.mktemp("script_with_launchers")))
tmpdir = tmpdir_factory.mktemp("script_with_launchers")
script = script_factory(tmpdir.joinpath("workspace"))
# Re-install pip so we get the launchers.
script.pip_install_local("-f", common_wheels, pip_src)
Expand All @@ -112,15 +112,15 @@ def test_completion_for_supported_shells(

@pytest.fixture(scope="session")
def autocomplete_script(
tmpdir_factory: pytest.TempdirFactory, script_factory: ScriptFactory
tmpdir_factory: pytest.TempPathFactory, script_factory: ScriptFactory
) -> PipTestEnvironment:
tmpdir = Path(str(tmpdir_factory.mktemp("autocomplete_script")))
tmpdir = tmpdir_factory.mktemp("autocomplete_script")
return script_factory(tmpdir.joinpath("workspace"))


class DoAutocomplete(Protocol):
def __call__(
self, words: str, cword: str, cwd: Optional[str] = None
self, words: str, cword: str, cwd: Union[Path, str, None] = None
) -> Tuple[TestPipResult, PipTestEnvironment]:
...

Expand All @@ -133,7 +133,7 @@ def autocomplete(
autocomplete_script.environ["PIP_AUTO_COMPLETE"] = "1"

def do_autocomplete(
words: str, cword: str, cwd: Optional[str] = None
words: str, cword: str, cwd: Union[Path, str, None] = None
) -> Tuple[TestPipResult, PipTestEnvironment]:
autocomplete_script.environ["COMP_WORDS"] = words
autocomplete_script.environ["COMP_CWORD"] = cword
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/test_config_settings.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import json
from pathlib import Path
from typing import Tuple
from zipfile import ZipFile

from tests.lib import PipTestEnvironment
from tests.lib.path import Path

PYPROJECT_TOML = """\
[build-system]
Expand Down
Loading

0 comments on commit ba11a20

Please sign in to comment.