import itertools import os import stat import tempfile import pytest from pip._internal.utils import temp_dir from pip._internal.utils.misc import ensure_dir from pip._internal.utils.temp_dir import ( AdjacentTempDirectory, TempDirectory, _default, global_tempdir_manager, tempdir_registry, ) # No need to test symlinked directories on Windows @pytest.mark.skipif("sys.platform == 'win32'") def test_symlinked_path(): with TempDirectory() as tmp_dir: assert os.path.exists(tmp_dir.path) alt_tmp_dir = tempfile.mkdtemp(prefix="pip-test-") assert ( os.path.dirname(tmp_dir.path) == os.path.dirname(os.path.realpath(alt_tmp_dir)) ) # are we on a system where /tmp is a symlink if os.path.realpath(alt_tmp_dir) != os.path.abspath(alt_tmp_dir): assert ( os.path.dirname(tmp_dir.path) != os.path.dirname(alt_tmp_dir) ) else: assert ( os.path.dirname(tmp_dir.path) == os.path.dirname(alt_tmp_dir) ) os.rmdir(tmp_dir.path) assert not os.path.exists(tmp_dir.path) def test_deletes_readonly_files(): def create_file(*args): fpath = os.path.join(*args) ensure_dir(os.path.dirname(fpath)) with open(fpath, "w") as f: f.write("Holla!") def readonly_file(*args): 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") def test_path_access_after_context_raises(): with TempDirectory() as tmp_dir: path = tmp_dir.path with pytest.raises(AssertionError) as e: _ = tmp_dir.path assert path in str(e.value) def test_path_access_after_clean_raises(): 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) def test_create_and_cleanup_work(): 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) @pytest.mark.parametrize("name", [ "ABC", "ABC.dist-info", "_+-", "_package", "A......B", "AB", "A", "2", ]) def test_adjacent_directory_names(name): def names(): 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 same_len = list(itertools.takewhile( lambda x: len(x) == len(name), some_names )) assert len(same_len) > 10 # Check the first group are correct expected_names = ['~' + name[1:]] expected_names.extend('~' + c + name[2:] for c in chars) 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:]) else: # All names are going to end with our original assert all(x.endswith(name) for x in some_names) @pytest.mark.parametrize("name", [ "A", "ABC", "ABC.dist-info", "_+-", "_package", ]) def test_adjacent_directory_exists(name, tmpdir): block_name, expect_name = itertools.islice( AdjacentTempDirectory._generate_names(name), 2) original = os.path.join(tmpdir, name) blocker = os.path.join(tmpdir, block_name) ensure_dir(original) ensure_dir(blocker) with AdjacentTempDirectory(original) as atmp_dir: assert expect_name == os.path.split(atmp_dir.path)[1] def test_adjacent_directory_permission_error(monkeypatch): name = "ABC" def raising_mkdir(*args, **kwargs): 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) with pytest.raises(OSError): with AdjacentTempDirectory(original): pass def test_global_tempdir_manager(): with global_tempdir_manager(): d = TempDirectory(globally_managed=True) path = d.path assert os.path.exists(path) assert not os.path.exists(path) def test_tempdirectory_asserts_global_tempdir(monkeypatch): monkeypatch.setattr(temp_dir, "_tempdir_manager", None) with pytest.raises(AssertionError): TempDirectory(globally_managed=True) deleted_kind = "deleted" not_deleted_kind = "not-deleted" @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), ]) def test_tempdir_registry(kind, delete, exists): 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 @pytest.mark.parametrize("delete,exists", [ (_default, True), (None, False) ]) def test_temp_dir_does_not_delete_explicit_paths_by_default( tmpdir, delete, exists ): 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 @pytest.mark.parametrize("should_delete", [True, False]) def test_tempdir_registry_lazy(should_delete): """ 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)