Skip to content

Commit 42359a9

Browse files
committed
Migrate tests to use pathlib.Path
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.
1 parent e58a8a5 commit 42359a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+563
-733
lines changed

tests/conftest.py

+59-34
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@
88
import sys
99
import time
1010
from contextlib import ExitStack, contextmanager
11-
from typing import TYPE_CHECKING, Callable, Dict, Iterable, Iterator, List, Optional
11+
from pathlib import Path
12+
from typing import (
13+
TYPE_CHECKING,
14+
Callable,
15+
Dict,
16+
Iterable,
17+
Iterator,
18+
List,
19+
Optional,
20+
Union,
21+
)
1222
from unittest.mock import patch
1323

14-
import py.path
1524
import pytest
1625

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

147155

148156
@pytest.fixture(scope="session")
149-
def tmpdir_factory(
150-
request: pytest.FixtureRequest, tmpdir_factory: pytest.TempdirFactory
151-
) -> Iterator[pytest.TempdirFactory]:
157+
def tmp_path_factory(
158+
request: pytest.FixtureRequest, tmp_path_factory: pytest.TempPathFactory
159+
) -> Iterator[pytest.TempPathFactory]:
152160
"""Modified `tmpdir_factory` session fixture
153161
that will automatically cleanup after itself.
154162
"""
155-
yield tmpdir_factory
163+
yield tmp_path_factory
156164
if not request.config.getoption("--keep-tmpdir"):
157165
shutil.rmtree(
158-
tmpdir_factory.getbasetemp(),
166+
tmp_path_factory.getbasetemp(),
159167
ignore_errors=True,
160168
)
161169

162170

171+
@pytest.fixture(scope="session")
172+
def tmpdir_factory(tmp_path_factory: pytest.TempPathFactory) -> pytest.TempPathFactory:
173+
"""Override Pytest's ``tmpdir_factory`` with our pathlib implementation.
174+
175+
This prevents mis-use of this fixture.
176+
"""
177+
return tmp_path_factory
178+
179+
163180
@pytest.fixture
164-
def tmpdir(request: pytest.FixtureRequest, tmpdir: py.path.local) -> Iterator[Path]:
181+
def tmp_path(request: pytest.FixtureRequest, tmp_path: Path) -> Iterator[Path]:
165182
"""
166183
Return a temporary directory path object which is unique to each test
167184
function invocation, created as a sub directory of the base temporary
168-
directory. The returned object is a ``tests.lib.path.Path`` object.
185+
directory. The returned object is a ``Path`` object.
169186
170-
This uses the built-in tmpdir fixture from pytest itself but modified
171-
to return our typical path object instead of py.path.local as well as
172-
deleting the temporary directories at the end of each test case.
187+
This uses the built-in tmp_path fixture from pytest itself, but deletes the
188+
temporary directories at the end of each test case.
173189
"""
174-
assert tmpdir.isdir()
175-
yield Path(str(tmpdir))
190+
assert tmp_path.is_dir()
191+
yield tmp_path
176192
# Clear out the temporary directory after the test has finished using it.
177193
# This should prevent us from needing a multiple gigabyte temporary
178194
# directory while running the tests.
179195
if not request.config.getoption("--keep-tmpdir"):
180-
tmpdir.remove(ignore_errors=True)
196+
shutil.rmtree(tmp_path, ignore_errors=True)
197+
198+
199+
@pytest.fixture()
200+
def tmpdir(tmp_path: Path) -> Path:
201+
"""Override Pytest's ``tmpdir`` with our pathlib implementation.
202+
203+
This prevents mis-use of this fixture.
204+
"""
205+
return tmp_path
181206

182207

183208
@pytest.fixture(autouse=True)
@@ -296,7 +321,7 @@ def scoped_global_tempdir_manager(request: pytest.FixtureRequest) -> Iterator[No
296321

297322

298323
@pytest.fixture(scope="session")
299-
def pip_src(tmpdir_factory: pytest.TempdirFactory) -> Path:
324+
def pip_src(tmpdir_factory: pytest.TempPathFactory) -> Path:
300325
def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]:
301326
# In the root directory...
302327
if path == SRC_DIR:
@@ -317,7 +342,7 @@ def not_code_files_and_folders(path: str, names: List[str]) -> Iterable[str]:
317342
ignored.update(fnmatch.filter(names, pattern))
318343
return ignored
319344

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

329354

330355
def _common_wheel_editable_install(
331-
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path, package: str
356+
tmpdir_factory: pytest.TempPathFactory, common_wheels: Path, package: str
332357
) -> Path:
333358
wheel_candidates = list(common_wheels.glob(f"{package}-*.whl"))
334359
assert len(wheel_candidates) == 1, wheel_candidates
335-
install_dir = Path(str(tmpdir_factory.mktemp(package))) / "install"
360+
install_dir = tmpdir_factory.mktemp(package) / "install"
336361
Wheel(wheel_candidates[0]).install_as_egg(install_dir)
337362
(install_dir / "EGG-INFO").rename(install_dir / f"{package}.egg-info")
338363
assert compileall.compile_dir(str(install_dir), quiet=1)
@@ -341,19 +366,19 @@ def _common_wheel_editable_install(
341366

342367
@pytest.fixture(scope="session")
343368
def setuptools_install(
344-
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path
369+
tmpdir_factory: pytest.TempPathFactory, common_wheels: Path
345370
) -> Path:
346371
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "setuptools")
347372

348373

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

353378

354379
@pytest.fixture(scope="session")
355380
def coverage_install(
356-
tmpdir_factory: pytest.TempdirFactory, common_wheels: Path
381+
tmpdir_factory: pytest.TempPathFactory, common_wheels: Path
357382
) -> Path:
358383
return _common_wheel_editable_install(tmpdir_factory, common_wheels, "coverage")
359384

@@ -370,7 +395,7 @@ def install_egg_link(
370395
@pytest.fixture(scope="session")
371396
def virtualenv_template(
372397
request: pytest.FixtureRequest,
373-
tmpdir_factory: pytest.TempdirFactory,
398+
tmpdir_factory: pytest.TempPathFactory,
374399
pip_src: Path,
375400
setuptools_install: Path,
376401
coverage_install: Path,
@@ -383,12 +408,12 @@ def virtualenv_template(
383408
venv_type = "virtualenv"
384409

385410
# Create the virtual environment
386-
tmpdir = Path(str(tmpdir_factory.mktemp("virtualenv")))
411+
tmpdir = tmpdir_factory.mktemp("virtualenv")
387412
venv = VirtualEnvironment(tmpdir.joinpath("venv_orig"), venv_type=venv_type)
388413

389414
# Install setuptools and pip.
390415
install_egg_link(venv, "setuptools", setuptools_install)
391-
pip_editable = Path(str(tmpdir_factory.mktemp("pip"))) / "pip"
416+
pip_editable = tmpdir_factory.mktemp("pip") / "pip"
392417
shutil.copytree(pip_src, pip_editable, symlinks=True)
393418
# noxfile.py is Python 3 only
394419
assert compileall.compile_dir(
@@ -397,7 +422,7 @@ def virtualenv_template(
397422
rx=re.compile("noxfile.py$"),
398423
)
399424
subprocess.check_call(
400-
[venv.bin / "python", "setup.py", "-q", "develop"], cwd=pip_editable
425+
[os.fspath(venv.bin / "python"), "setup.py", "-q", "develop"], cwd=pip_editable
401426
)
402427

403428
# Install coverage and pth file for executing it in any spawned processes
@@ -493,7 +518,7 @@ def factory(
493518
def script(
494519
tmpdir: Path,
495520
virtualenv: VirtualEnvironment,
496-
script_factory: Callable[[Path, Optional[VirtualEnvironment]], PipTestEnvironment],
521+
script_factory: ScriptFactory,
497522
) -> PipTestEnvironment:
498523
"""
499524
Return a PipTestEnvironment which is unique to each test function and
@@ -511,8 +536,8 @@ def common_wheels() -> Path:
511536

512537

513538
@pytest.fixture(scope="session")
514-
def shared_data(tmpdir_factory: pytest.TempdirFactory) -> TestData:
515-
return TestData.copy(Path(str(tmpdir_factory.mktemp("data"))))
539+
def shared_data(tmpdir_factory: pytest.TempPathFactory) -> TestData:
540+
return TestData.copy(tmpdir_factory.mktemp("data"))
516541

517542

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

528553

529554
class InMemoryPip:
530-
def pip(self, *args: str) -> InMemoryPipResult:
555+
def pip(self, *args: Union[str, Path]) -> InMemoryPipResult:
531556
orig_stdout = sys.stdout
532557
stdout = io.StringIO()
533558
sys.stdout = stdout
534559
try:
535-
returncode = pip_entry_point(list(args))
560+
returncode = pip_entry_point([os.fspath(a) for a in args])
536561
except SystemExit as e:
537562
returncode = e.code or 0
538563
finally:
@@ -555,15 +580,15 @@ def deprecated_python() -> bool:
555580

556581

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

564589
def factory() -> str:
565590
"""Returns path to cert/key file."""
566-
output_path = Path(str(tmpdir_factory.mktemp("certs"))) / "cert.pem"
591+
output_path = tmpdir_factory.mktemp("certs") / "cert.pem"
567592
# Must be Text on PY2.
568593
cert, key = make_tls_cert("localhost")
569594
with open(str(output_path), "wb") as f:

tests/functional/test_broken_stdout.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import os
22
import subprocess
3+
from pathlib import Path
34
from typing import List, Tuple
45

5-
from tests.lib.path import Path
6-
76
_BROKEN_STDOUT_RETURN_CODE = 120
87

98

tests/functional/test_build_env.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from textwrap import dedent
23
from typing import Optional
34

@@ -69,11 +70,11 @@ def run_with_build_env(
6970
" ",
7071
)
7172
)
72-
args = ["python", build_env_script]
73+
args = ["python", os.fspath(build_env_script)]
7374
if test_script_contents is not None:
7475
test_script = script.scratch_path / "test.py"
7576
test_script.write_text(dedent(test_script_contents))
76-
args.append(test_script)
77+
args.append(os.fspath(test_script))
7778
return script.run(*args)
7879

7980

@@ -89,7 +90,7 @@ def test_build_env_allow_empty_requirements_install() -> None:
8990
def test_build_env_allow_only_one_install(script: PipTestEnvironment) -> None:
9091
create_basic_wheel_for_package(script, "foo", "1.0")
9192
create_basic_wheel_for_package(script, "bar", "1.0")
92-
finder = make_test_finder(find_links=[script.scratch_path])
93+
finder = make_test_finder(find_links=[os.fspath(script.scratch_path)])
9394
build_env = BuildEnvironment()
9495
for prefix in ("normal", "overlay"):
9596
build_env.install_requirements(

tests/functional/test_completion.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import os
22
import sys
3-
from typing import TYPE_CHECKING, Optional, Tuple
3+
from pathlib import Path
4+
from typing import TYPE_CHECKING, Tuple, Union
45

56
import pytest
67

78
from tests.conftest import ScriptFactory
89
from tests.lib import PipTestEnvironment, TestData, TestPipResult
9-
from tests.lib.path import Path
1010

1111
if TYPE_CHECKING:
1212
from typing import Protocol
@@ -83,12 +83,12 @@
8383

8484
@pytest.fixture(scope="session")
8585
def script_with_launchers(
86-
tmpdir_factory: pytest.TempdirFactory,
86+
tmpdir_factory: pytest.TempPathFactory,
8787
script_factory: ScriptFactory,
8888
common_wheels: Path,
8989
pip_src: Path,
9090
) -> PipTestEnvironment:
91-
tmpdir = Path(str(tmpdir_factory.mktemp("script_with_launchers")))
91+
tmpdir = tmpdir_factory.mktemp("script_with_launchers")
9292
script = script_factory(tmpdir.joinpath("workspace"))
9393
# Re-install pip so we get the launchers.
9494
script.pip_install_local("-f", common_wheels, pip_src)
@@ -112,15 +112,15 @@ def test_completion_for_supported_shells(
112112

113113
@pytest.fixture(scope="session")
114114
def autocomplete_script(
115-
tmpdir_factory: pytest.TempdirFactory, script_factory: ScriptFactory
115+
tmpdir_factory: pytest.TempPathFactory, script_factory: ScriptFactory
116116
) -> PipTestEnvironment:
117-
tmpdir = Path(str(tmpdir_factory.mktemp("autocomplete_script")))
117+
tmpdir = tmpdir_factory.mktemp("autocomplete_script")
118118
return script_factory(tmpdir.joinpath("workspace"))
119119

120120

121121
class DoAutocomplete(Protocol):
122122
def __call__(
123-
self, words: str, cword: str, cwd: Optional[str] = None
123+
self, words: str, cword: str, cwd: Union[Path, str, None] = None
124124
) -> Tuple[TestPipResult, PipTestEnvironment]:
125125
...
126126

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

135135
def do_autocomplete(
136-
words: str, cword: str, cwd: Optional[str] = None
136+
words: str, cword: str, cwd: Union[Path, str, None] = None
137137
) -> Tuple[TestPipResult, PipTestEnvironment]:
138138
autocomplete_script.environ["COMP_WORDS"] = words
139139
autocomplete_script.environ["COMP_CWORD"] = cword

tests/functional/test_config_settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import json
2+
from pathlib import Path
23
from typing import Tuple
34
from zipfile import ZipFile
45

56
from tests.lib import PipTestEnvironment
6-
from tests.lib.path import Path
77

88
PYPROJECT_TOML = """\
99
[build-system]

0 commit comments

Comments
 (0)