Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate tests to use pathlib.Path #11170

Merged
merged 2 commits into from
Jun 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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