2019-09-24 00:47:28 +02:00
|
|
|
import itertools
|
|
|
|
import os
|
|
|
|
import stat
|
|
|
|
import tempfile
|
2021-08-30 00:43:28 +02:00
|
|
|
from typing import Any, Iterator, Optional, Union
|
2019-09-24 00:47:28 +02:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2019-12-08 03:08:26 +01:00
|
|
|
from pip._internal.utils import temp_dir
|
2019-09-24 00:47:28 +02:00
|
|
|
from pip._internal.utils.misc import ensure_dir
|
2019-12-08 03:08:26 +01:00
|
|
|
from pip._internal.utils.temp_dir import (
|
|
|
|
AdjacentTempDirectory,
|
|
|
|
TempDirectory,
|
2021-08-30 00:43:28 +02:00
|
|
|
_Default,
|
2020-01-25 20:01:32 +01:00
|
|
|
_default,
|
2019-12-08 03:08:26 +01:00
|
|
|
global_tempdir_manager,
|
2019-12-14 04:05:22 +01:00
|
|
|
tempdir_registry,
|
2019-12-08 03:08:26 +01:00
|
|
|
)
|
2021-08-30 00:43:28 +02:00
|
|
|
from tests.lib.path import Path
|
2019-09-24 00:47:28 +02:00
|
|
|
|
|
|
|
|
2019-09-24 00:49:12 +02:00
|
|
|
# No need to test symlinked directories on Windows
|
|
|
|
@pytest.mark.skipif("sys.platform == 'win32'")
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_symlinked_path() -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
with TempDirectory() as tmp_dir:
|
|
|
|
assert os.path.exists(tmp_dir.path)
|
|
|
|
|
|
|
|
alt_tmp_dir = tempfile.mkdtemp(prefix="pip-test-")
|
2021-08-13 15:23:45 +02:00
|
|
|
assert os.path.dirname(tmp_dir.path) == os.path.dirname(
|
|
|
|
os.path.realpath(alt_tmp_dir)
|
2019-09-24 00:49:12 +02:00
|
|
|
)
|
|
|
|
# are we on a system where /tmp is a symlink
|
|
|
|
if os.path.realpath(alt_tmp_dir) != os.path.abspath(alt_tmp_dir):
|
2021-08-13 15:23:45 +02:00
|
|
|
assert os.path.dirname(tmp_dir.path) != os.path.dirname(alt_tmp_dir)
|
2019-09-24 00:49:12 +02:00
|
|
|
else:
|
2021-08-13 15:23:45 +02:00
|
|
|
assert os.path.dirname(tmp_dir.path) == os.path.dirname(alt_tmp_dir)
|
2019-09-24 00:49:12 +02:00
|
|
|
os.rmdir(tmp_dir.path)
|
2019-09-24 00:47:28 +02:00
|
|
|
assert not os.path.exists(tmp_dir.path)
|
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_deletes_readonly_files() -> None:
|
|
|
|
def create_file(*args: str) -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
fpath = os.path.join(*args)
|
|
|
|
ensure_dir(os.path.dirname(fpath))
|
|
|
|
with open(fpath, "w") as f:
|
|
|
|
f.write("Holla!")
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def readonly_file(*args: str) -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
fpath = os.path.join(*args)
|
|
|
|
os.chmod(fpath, stat.S_IREAD)
|
|
|
|
|
|
|
|
with TempDirectory() as tmp_dir:
|
|
|
|
create_file(tmp_dir.path, "normal-file")
|
|
|
|
create_file(tmp_dir.path, "readonly-file")
|
|
|
|
readonly_file(tmp_dir.path, "readonly-file")
|
|
|
|
|
|
|
|
create_file(tmp_dir.path, "subfolder", "normal-file")
|
|
|
|
create_file(tmp_dir.path, "subfolder", "readonly-file")
|
|
|
|
readonly_file(tmp_dir.path, "subfolder", "readonly-file")
|
|
|
|
|
2019-09-24 02:00:01 +02:00
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_path_access_after_context_raises() -> None:
|
2019-09-24 02:00:01 +02:00
|
|
|
with TempDirectory() as tmp_dir:
|
|
|
|
path = tmp_dir.path
|
|
|
|
|
|
|
|
with pytest.raises(AssertionError) as e:
|
|
|
|
_ = tmp_dir.path
|
|
|
|
|
|
|
|
assert path in str(e.value)
|
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_path_access_after_clean_raises() -> None:
|
2019-09-24 02:00:01 +02:00
|
|
|
tmp_dir = TempDirectory()
|
|
|
|
path = tmp_dir.path
|
|
|
|
tmp_dir.cleanup()
|
|
|
|
|
|
|
|
with pytest.raises(AssertionError) as e:
|
|
|
|
_ = tmp_dir.path
|
|
|
|
|
|
|
|
assert path in str(e.value)
|
2019-09-24 00:49:12 +02:00
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_create_and_cleanup_work() -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
tmp_dir = TempDirectory()
|
|
|
|
created_path = tmp_dir.path
|
|
|
|
|
|
|
|
assert tmp_dir.path is not None
|
|
|
|
assert os.path.exists(created_path)
|
|
|
|
|
|
|
|
tmp_dir.cleanup()
|
|
|
|
assert not os.path.exists(created_path)
|
|
|
|
|
|
|
|
|
2021-08-13 15:23:45 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"name",
|
|
|
|
[
|
|
|
|
"ABC",
|
|
|
|
"ABC.dist-info",
|
|
|
|
"_+-",
|
|
|
|
"_package",
|
|
|
|
"A......B",
|
|
|
|
"AB",
|
|
|
|
"A",
|
|
|
|
"2",
|
|
|
|
],
|
|
|
|
)
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_adjacent_directory_names(name: str) -> None:
|
|
|
|
def names() -> Iterator[str]:
|
2019-09-24 00:49:12 +02:00
|
|
|
return AdjacentTempDirectory._generate_names(name)
|
|
|
|
|
|
|
|
chars = AdjacentTempDirectory.LEADING_CHARS
|
|
|
|
|
|
|
|
# Ensure many names are unique
|
|
|
|
# (For long *name*, this sequence can be extremely long.
|
|
|
|
# However, since we're only ever going to take the first
|
|
|
|
# result that works, provided there are many of those
|
|
|
|
# and that shorter names result in totally unique sets,
|
|
|
|
# it's okay to skip part of the test.)
|
|
|
|
some_names = list(itertools.islice(names(), 1000))
|
|
|
|
# We should always get at least 1000 names
|
|
|
|
assert len(some_names) == 1000
|
|
|
|
|
|
|
|
# Ensure original name does not appear early in the set
|
|
|
|
assert name not in some_names
|
|
|
|
|
|
|
|
if len(name) > 2:
|
|
|
|
# Names should be at least 90% unique (given the infinite
|
|
|
|
# range of inputs, and the possibility that generated names
|
|
|
|
# may already exist on disk anyway, this is a much cheaper
|
|
|
|
# criteria to enforce than complete uniqueness).
|
|
|
|
assert len(some_names) > 0.9 * len(set(some_names))
|
|
|
|
|
|
|
|
# Ensure the first few names are the same length as the original
|
2021-08-13 15:23:45 +02:00
|
|
|
same_len = list(itertools.takewhile(lambda x: len(x) == len(name), some_names))
|
2019-09-24 00:49:12 +02:00
|
|
|
assert len(same_len) > 10
|
|
|
|
|
|
|
|
# Check the first group are correct
|
2021-08-13 15:23:45 +02:00
|
|
|
expected_names = ["~" + name[1:]]
|
|
|
|
expected_names.extend("~" + c + name[2:] for c in chars)
|
2019-09-24 00:49:12 +02:00
|
|
|
for x, y in zip(some_names, expected_names):
|
|
|
|
assert x == y
|
|
|
|
|
|
|
|
else:
|
|
|
|
# All names are going to be longer than our original
|
|
|
|
assert min(len(x) for x in some_names) > 1
|
|
|
|
|
|
|
|
# All names are going to be unique
|
|
|
|
assert len(some_names) == len(set(some_names))
|
|
|
|
|
|
|
|
if len(name) == 2:
|
|
|
|
# All but the first name are going to end with our original
|
|
|
|
assert all(x.endswith(name) for x in some_names[1:])
|
2019-09-24 00:47:28 +02:00
|
|
|
else:
|
2019-09-24 00:49:12 +02:00
|
|
|
# All names are going to end with our original
|
|
|
|
assert all(x.endswith(name) for x in some_names)
|
2019-09-24 00:47:28 +02:00
|
|
|
|
|
|
|
|
2021-08-13 15:23:45 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"name",
|
|
|
|
[
|
|
|
|
"A",
|
|
|
|
"ABC",
|
|
|
|
"ABC.dist-info",
|
|
|
|
"_+-",
|
|
|
|
"_package",
|
|
|
|
],
|
|
|
|
)
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_adjacent_directory_exists(name: str, tmpdir: Path) -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
block_name, expect_name = itertools.islice(
|
2021-08-13 15:23:45 +02:00
|
|
|
AdjacentTempDirectory._generate_names(name), 2
|
|
|
|
)
|
2019-09-24 00:49:12 +02:00
|
|
|
|
|
|
|
original = os.path.join(tmpdir, name)
|
|
|
|
blocker = os.path.join(tmpdir, block_name)
|
|
|
|
|
|
|
|
ensure_dir(original)
|
|
|
|
ensure_dir(blocker)
|
2019-09-24 00:47:28 +02:00
|
|
|
|
2019-09-24 00:49:12 +02:00
|
|
|
with AdjacentTempDirectory(original) as atmp_dir:
|
|
|
|
assert expect_name == os.path.split(atmp_dir.path)[1]
|
2019-09-24 00:47:28 +02:00
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_adjacent_directory_permission_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
name = "ABC"
|
2019-09-24 00:47:28 +02:00
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def raising_mkdir(*args: Any, **kwargs: Any) -> None:
|
2019-09-24 00:49:12 +02:00
|
|
|
raise OSError("Unknown OSError")
|
|
|
|
|
|
|
|
with TempDirectory() as tmp_dir:
|
|
|
|
original = os.path.join(tmp_dir.path, name)
|
|
|
|
|
|
|
|
ensure_dir(original)
|
|
|
|
monkeypatch.setattr("os.mkdir", raising_mkdir)
|
2019-09-24 00:47:28 +02:00
|
|
|
|
2019-09-24 00:49:12 +02:00
|
|
|
with pytest.raises(OSError):
|
|
|
|
with AdjacentTempDirectory(original):
|
|
|
|
pass
|
2019-12-08 03:08:26 +01:00
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_global_tempdir_manager() -> None:
|
2019-12-08 03:08:26 +01:00
|
|
|
with global_tempdir_manager():
|
|
|
|
d = TempDirectory(globally_managed=True)
|
|
|
|
path = d.path
|
|
|
|
assert os.path.exists(path)
|
|
|
|
assert not os.path.exists(path)
|
|
|
|
|
|
|
|
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_tempdirectory_asserts_global_tempdir(monkeypatch: pytest.MonkeyPatch) -> None:
|
2019-12-08 03:08:26 +01:00
|
|
|
monkeypatch.setattr(temp_dir, "_tempdir_manager", None)
|
|
|
|
with pytest.raises(AssertionError):
|
|
|
|
TempDirectory(globally_managed=True)
|
2019-12-14 04:05:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
deleted_kind = "deleted"
|
|
|
|
not_deleted_kind = "not-deleted"
|
|
|
|
|
|
|
|
|
2021-08-13 15:23:45 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"delete,kind,exists",
|
|
|
|
[
|
|
|
|
(None, deleted_kind, False),
|
|
|
|
(_default, deleted_kind, False),
|
|
|
|
(True, deleted_kind, False),
|
|
|
|
(False, deleted_kind, True),
|
|
|
|
(None, not_deleted_kind, True),
|
|
|
|
(_default, not_deleted_kind, True),
|
|
|
|
(True, not_deleted_kind, False),
|
|
|
|
(False, not_deleted_kind, True),
|
|
|
|
(None, "unspecified", False),
|
|
|
|
(_default, "unspecified", False),
|
|
|
|
(True, "unspecified", False),
|
|
|
|
(False, "unspecified", True),
|
|
|
|
],
|
|
|
|
)
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_tempdir_registry(
|
|
|
|
delete: Union[bool, _Default], kind: str, exists: bool
|
|
|
|
) -> None:
|
2019-12-14 04:05:22 +01:00
|
|
|
with tempdir_registry() as registry:
|
|
|
|
registry.set_delete(deleted_kind, True)
|
|
|
|
registry.set_delete(not_deleted_kind, False)
|
|
|
|
|
|
|
|
with TempDirectory(delete=delete, kind=kind) as d:
|
|
|
|
path = d.path
|
|
|
|
assert os.path.exists(path)
|
|
|
|
assert os.path.exists(path) == exists
|
2020-01-19 12:22:14 +01:00
|
|
|
|
|
|
|
|
2021-08-13 15:23:45 +02:00
|
|
|
@pytest.mark.parametrize("delete,exists", [(_default, True), (None, False)])
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_temp_dir_does_not_delete_explicit_paths_by_default(
|
|
|
|
tmpdir: Path, delete: Optional[_Default], exists: bool
|
|
|
|
) -> None:
|
2020-01-25 20:01:32 +01:00
|
|
|
path = tmpdir / "example"
|
|
|
|
path.mkdir()
|
|
|
|
|
|
|
|
with tempdir_registry() as registry:
|
|
|
|
registry.set_delete(deleted_kind, True)
|
|
|
|
|
|
|
|
with TempDirectory(path=path, delete=delete, kind=deleted_kind) as d:
|
|
|
|
assert str(d.path) == path
|
|
|
|
assert os.path.exists(path)
|
|
|
|
assert os.path.exists(path) == exists
|
|
|
|
|
|
|
|
|
2020-01-19 12:22:14 +01:00
|
|
|
@pytest.mark.parametrize("should_delete", [True, False])
|
2021-08-30 00:43:28 +02:00
|
|
|
def test_tempdir_registry_lazy(should_delete: bool) -> None:
|
2020-01-19 12:22:14 +01:00
|
|
|
"""
|
|
|
|
Test the registry entry can be updated after a temp dir is created,
|
|
|
|
to change whether a kind should be deleted or not.
|
|
|
|
"""
|
|
|
|
with tempdir_registry() as registry:
|
|
|
|
with TempDirectory(delete=None, kind="test-for-lazy") as d:
|
|
|
|
path = d.path
|
|
|
|
registry.set_delete("test-for-lazy", should_delete)
|
|
|
|
assert os.path.exists(path)
|
|
|
|
assert os.path.exists(path) == (not should_delete)
|