superpathlib 2.0.3__tar.gz → 2.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {superpathlib-2.0.3/src/superpathlib.egg-info → superpathlib-2.0.5}/PKG-INFO +3 -3
- {superpathlib-2.0.3 → superpathlib-2.0.5}/pyproject.toml +11 -10
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/base.py +7 -7
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/cached_content.py +4 -4
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/common_folders.py +1 -1
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/content_properties.py +7 -6
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/encryption.py +17 -9
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/extra_functionality.py +82 -72
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/metadata_properties.py +14 -13
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/override.py +12 -8
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/tags.py +1 -1
- {superpathlib-2.0.3 → superpathlib-2.0.5/src/superpathlib.egg-info}/PKG-INFO +3 -3
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib.egg-info/requires.txt +2 -2
- {superpathlib-2.0.3 → superpathlib-2.0.5}/tests/test_cached_content.py +9 -5
- {superpathlib-2.0.3 → superpathlib-2.0.5}/tests/test_common_folders.py +3 -2
- {superpathlib-2.0.3 → superpathlib-2.0.5}/tests/test_content.py +4 -3
- {superpathlib-2.0.3 → superpathlib-2.0.5}/tests/test_encrypted_content.py +2 -1
- {superpathlib-2.0.3 → superpathlib-2.0.5}/tests/test_functionality.py +20 -9
- {superpathlib-2.0.3 → superpathlib-2.0.5}/tests/test_metadata.py +3 -2
- {superpathlib-2.0.3 → superpathlib-2.0.5}/LICENSE +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/README.md +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/setup.cfg +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/__init__.py +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/path.py +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/py.typed +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib/utils.py +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib.egg-info/SOURCES.txt +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib.egg-info/dependency_links.txt +0 -0
- {superpathlib-2.0.3 → superpathlib-2.0.5}/src/superpathlib.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: superpathlib
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Extended Pathlib
|
|
5
5
|
Author-email: Quinten Roets <qdr2104@columbia.edu>
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ License-File: LICENSE
|
|
|
11
11
|
Requires-Dist: simple-classproperty<5,>=4.0.2
|
|
12
12
|
Provides-Extra: full
|
|
13
13
|
Requires-Dist: dirhash<1,>=0.2.1; extra == "full"
|
|
14
|
-
Requires-Dist: numpy<
|
|
14
|
+
Requires-Dist: numpy<3,>=1.26.4; extra == "full"
|
|
15
15
|
Requires-Dist: package-utils<1,>=0.6.1; extra == "full"
|
|
16
16
|
Requires-Dist: PyYaml<7,>=6.0.1; extra == "full"
|
|
17
17
|
Requires-Dist: xattr<2,>=0.10.1; extra == "full"
|
|
@@ -20,7 +20,7 @@ Requires-Dist: hypothesis<7,>=6.97.1; extra == "dev"
|
|
|
20
20
|
Requires-Dist: package-dev-tools<1,>=0.5.12; extra == "dev"
|
|
21
21
|
Requires-Dist: types-PyYaml<7,>=6.0.12.12; extra == "dev"
|
|
22
22
|
Requires-Dist: dirhash<1,>=0.2.1; extra == "dev"
|
|
23
|
-
Requires-Dist: numpy<
|
|
23
|
+
Requires-Dist: numpy<3,>=1.26.4; extra == "dev"
|
|
24
24
|
Requires-Dist: package-utils<1,>=0.6.1; extra == "dev"
|
|
25
25
|
Requires-Dist: PyYaml<7,>=6.0.1; extra == "dev"
|
|
26
26
|
Requires-Dist: xattr<2,>=0.10.1; extra == "dev"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "superpathlib"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.5"
|
|
4
4
|
description = "Extended Pathlib"
|
|
5
5
|
authors = [{name = "Quinten Roets", email = "qdr2104@columbia.edu"}]
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
|
13
13
|
[project.optional-dependencies]
|
|
14
14
|
full = [
|
|
15
15
|
"dirhash >=0.2.1, <1",
|
|
16
|
-
"numpy >=1.26.4, <
|
|
16
|
+
"numpy >=1.26.4, <3",
|
|
17
17
|
"package-utils >=0.6.1, <1",
|
|
18
18
|
"PyYaml >=6.0.1, <7",
|
|
19
19
|
"xattr >=0.10.1, <2",
|
|
@@ -27,7 +27,7 @@ dev = [
|
|
|
27
27
|
|
|
28
28
|
# full
|
|
29
29
|
"dirhash >=0.2.1, <1",
|
|
30
|
-
"numpy >=1.26.4, <
|
|
30
|
+
"numpy >=1.26.4, <3",
|
|
31
31
|
"package-utils >=0.6.1, <1",
|
|
32
32
|
"PyYaml >=6.0.1, <7",
|
|
33
33
|
"xattr >=0.10.1, <2",
|
|
@@ -41,7 +41,7 @@ requires = ["setuptools"]
|
|
|
41
41
|
build-backend = "setuptools.build_meta"
|
|
42
42
|
|
|
43
43
|
[tool.coverage.run]
|
|
44
|
-
command_line = "-m pytest"
|
|
44
|
+
command_line = "-m pytest tests"
|
|
45
45
|
|
|
46
46
|
[tool.coverage.report]
|
|
47
47
|
precision = 4
|
|
@@ -67,12 +67,12 @@ pythonpath = [
|
|
|
67
67
|
fix = true
|
|
68
68
|
|
|
69
69
|
[tool.ruff.lint]
|
|
70
|
-
select = [
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
70
|
+
select = ["ALL"]
|
|
71
|
+
ignore = [
|
|
72
|
+
"ANN101", # annotate self
|
|
73
|
+
"ANN102", # annotate cls
|
|
74
|
+
"ANN401", # annotated with Any
|
|
75
|
+
"D", # docstrings
|
|
76
76
|
]
|
|
77
77
|
|
|
78
78
|
[tool.ruff.lint.isort]
|
|
@@ -80,6 +80,7 @@ known-first-party = ["src"]
|
|
|
80
80
|
|
|
81
81
|
[tool.ruff.lint.per-file-ignores]
|
|
82
82
|
"__init__.py" = ["F401"]
|
|
83
|
+
"tests/*" = ["S101"] # assert used
|
|
83
84
|
|
|
84
85
|
[tool.setuptools.package-data]
|
|
85
86
|
superpathlib = ["py.typed"]
|
|
@@ -3,21 +3,21 @@ import pathlib
|
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
class Path(pathlib.Path):
|
|
6
|
+
class Path(pathlib.Path): # pragma: nocover
|
|
7
7
|
"""
|
|
8
8
|
Extend pathlib functionality and enable further extensions by inheriting.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
# _flavour attribute explicitly required to inherit from pathlib
|
|
12
|
-
if sys.version_info
|
|
12
|
+
if sys.version_info < (3, 12):
|
|
13
13
|
_flavour = (
|
|
14
|
-
pathlib._windows_flavour # type: ignore[attr-defined] # noqa
|
|
14
|
+
pathlib._windows_flavour # type: ignore[attr-defined] # noqa: SLF001
|
|
15
15
|
if os.name == "nt"
|
|
16
|
-
else pathlib._posix_flavour # type: ignore[attr-defined] # noqa
|
|
16
|
+
else pathlib._posix_flavour # type: ignore[attr-defined] # noqa: SLF001
|
|
17
17
|
)
|
|
18
|
-
else:
|
|
18
|
+
else:
|
|
19
19
|
_flavour = (
|
|
20
|
-
pathlib.ntpath # type: ignore[attr-defined]
|
|
20
|
+
pathlib.ntpath # type: ignore[attr-defined]
|
|
21
21
|
if os.name == "nt"
|
|
22
|
-
else pathlib.posixpath # type: ignore[attr-defined]
|
|
22
|
+
else pathlib.posixpath # type: ignore[attr-defined]
|
|
23
23
|
)
|
|
@@ -21,12 +21,12 @@ class Path(metadata_properties.Path):
|
|
|
21
21
|
def cached_content(self) -> CachedFileContent[dict[str, str]]:
|
|
22
22
|
from package_utils.storage import CachedFileContent
|
|
23
23
|
|
|
24
|
-
return CachedFileContent(self, default={}) # type: ignore
|
|
24
|
+
return CachedFileContent(self, default={}) # type: ignore[arg-type]
|
|
25
25
|
|
|
26
26
|
def create_cached_content(self, default: T) -> CachedFileContent[T]:
|
|
27
27
|
from package_utils.storage import CachedFileContent
|
|
28
28
|
|
|
29
|
-
return CachedFileContent(self, default=default) # type: ignore
|
|
29
|
+
return CachedFileContent(self, default=default) # type: ignore[arg-type]
|
|
30
30
|
|
|
31
31
|
@cached_property
|
|
32
32
|
def cached_text(self) -> CachedFileContent[str]:
|
|
@@ -39,7 +39,7 @@ class Path(metadata_properties.Path):
|
|
|
39
39
|
self.text = content
|
|
40
40
|
|
|
41
41
|
return CachedFileContent(
|
|
42
|
-
self, # type: ignore
|
|
42
|
+
self, # type: ignore[arg-type]
|
|
43
43
|
default="",
|
|
44
44
|
load_function=load_function,
|
|
45
45
|
save_function=save_function,
|
|
@@ -56,7 +56,7 @@ class Path(metadata_properties.Path):
|
|
|
56
56
|
self.byte_content = content
|
|
57
57
|
|
|
58
58
|
return CachedFileContent(
|
|
59
|
-
self, # type: ignore
|
|
59
|
+
self, # type: ignore[arg-type]
|
|
60
60
|
default=b"",
|
|
61
61
|
load_function=load_function,
|
|
62
62
|
save_function=save_function,
|
|
@@ -2,12 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import typing
|
|
5
|
-
from collections.abc import Iterable
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
7
|
from . import base
|
|
9
8
|
|
|
10
9
|
if typing.TYPE_CHECKING: # pragma: nocover
|
|
10
|
+
from collections.abc import Iterable
|
|
11
|
+
|
|
11
12
|
from numpy.typing import NDArray
|
|
12
13
|
|
|
13
14
|
|
|
@@ -68,17 +69,17 @@ class Path(base.Path):
|
|
|
68
69
|
import yaml
|
|
69
70
|
|
|
70
71
|
# C implementation much faster but only supported on Linux
|
|
71
|
-
Loader: type[yaml.CFullLoader | yaml.FullLoader] = (
|
|
72
|
+
Loader: type[yaml.CFullLoader | yaml.FullLoader] = ( # noqa: N806
|
|
72
73
|
yaml.CFullLoader if hasattr(yaml, "CFullLoader") else yaml.FullLoader
|
|
73
74
|
)
|
|
74
|
-
return yaml.load(self.text, Loader=Loader) or {}
|
|
75
|
+
return yaml.load(self.text, Loader=Loader) or {} # noqa: S506
|
|
75
76
|
|
|
76
77
|
@yaml.setter
|
|
77
78
|
def yaml(self, value: dict[str, Any] | list[Any]) -> None:
|
|
78
79
|
import yaml
|
|
79
80
|
|
|
80
81
|
# C implementation much faster but only supported on Linux
|
|
81
|
-
Dumper = yaml.CDumper if hasattr(yaml, "CDumper") else yaml.Dumper
|
|
82
|
+
Dumper = yaml.CDumper if hasattr(yaml, "CDumper") else yaml.Dumper # noqa: N806
|
|
82
83
|
self.text = yaml.dump(value, Dumper=Dumper, width=1024)
|
|
83
84
|
|
|
84
85
|
@property
|
|
@@ -86,11 +87,11 @@ class Path(base.Path):
|
|
|
86
87
|
import numpy as np
|
|
87
88
|
|
|
88
89
|
with self.open("rb") as fp:
|
|
89
|
-
return np.load(fp) # type: ignore
|
|
90
|
+
return np.load(fp) # type: ignore[no-any-return]
|
|
90
91
|
|
|
91
92
|
@numpy.setter
|
|
92
93
|
def numpy(self, value: NDArray[Any]) -> None:
|
|
93
94
|
import numpy as np
|
|
94
95
|
|
|
95
96
|
with self.open("wb") as fp:
|
|
96
|
-
np.save(fp, value)
|
|
97
|
+
np.save(fp, value)
|
|
@@ -22,10 +22,10 @@ class EncryptedPath(Path):
|
|
|
22
22
|
@cached_property
|
|
23
23
|
def password(self) -> str: # pragma: nocover
|
|
24
24
|
if "GITHUB_ACTION" in os.environ:
|
|
25
|
-
password = "github_action_password"
|
|
25
|
+
password = "github_action_password" # noqa: S105
|
|
26
26
|
else:
|
|
27
27
|
command = 'ksshaskpass -- "Enter passphrase for file encryption: "'
|
|
28
|
-
password = subprocess.getoutput(command)
|
|
28
|
+
password = subprocess.getoutput(command) # noqa: S605
|
|
29
29
|
return password
|
|
30
30
|
|
|
31
31
|
@property
|
|
@@ -39,24 +39,32 @@ class EncryptedPath(Path):
|
|
|
39
39
|
def read_bytes(self) -> bytes:
|
|
40
40
|
encrypted_bytes = super().read_bytes()
|
|
41
41
|
if encrypted_bytes:
|
|
42
|
-
process = subprocess.Popen(
|
|
43
|
-
self.decryption_command,
|
|
42
|
+
process = subprocess.Popen( # noqa: S603
|
|
43
|
+
self.decryption_command,
|
|
44
|
+
stdin=subprocess.PIPE,
|
|
45
|
+
stdout=subprocess.PIPE,
|
|
44
46
|
)
|
|
45
47
|
decrypted_bytes, _ = process.communicate(input=encrypted_bytes)
|
|
46
48
|
else:
|
|
47
49
|
decrypted_bytes = encrypted_bytes
|
|
48
50
|
return decrypted_bytes
|
|
49
51
|
|
|
50
|
-
def write_bytes(self, data: bytes) -> int: # type: ignore
|
|
51
|
-
process = subprocess.Popen(
|
|
52
|
-
self.encryption_command,
|
|
52
|
+
def write_bytes(self, data: bytes) -> int: # type: ignore[override]
|
|
53
|
+
process = subprocess.Popen( # noqa: S603
|
|
54
|
+
self.encryption_command,
|
|
55
|
+
stdin=subprocess.PIPE,
|
|
56
|
+
stdout=subprocess.PIPE,
|
|
53
57
|
)
|
|
54
58
|
encrypted_data = process.communicate(input=data)[0]
|
|
55
59
|
return super().write_bytes(encrypted_data)
|
|
56
60
|
|
|
57
|
-
def read_text(
|
|
61
|
+
def read_text(
|
|
62
|
+
self,
|
|
63
|
+
encoding: str | None = None, # noqa: ARG002
|
|
64
|
+
errors: str | None = None, # noqa: ARG002
|
|
65
|
+
) -> str:
|
|
58
66
|
return self.read_bytes().decode()
|
|
59
67
|
|
|
60
|
-
def write_text(self, data: str, **_: Any) -> int: # type: ignore
|
|
68
|
+
def write_text(self, data: str, **_: Any) -> int: # type: ignore[override]
|
|
61
69
|
byte_data = data.encode()
|
|
62
70
|
return self.write_bytes(byte_data)
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import os
|
|
2
3
|
import shutil
|
|
3
4
|
import tempfile
|
|
4
5
|
import time
|
|
5
6
|
import typing
|
|
6
7
|
import urllib.parse
|
|
7
|
-
from collections.abc import Callable,
|
|
8
|
+
from collections.abc import Callable, Iterator
|
|
8
9
|
from functools import cached_property
|
|
9
10
|
from types import TracebackType
|
|
10
|
-
from typing import Any, TypeVar
|
|
11
|
+
from typing import Any, TypeVar, cast
|
|
11
12
|
|
|
12
13
|
from . import cached_content
|
|
13
14
|
from .utils import find_first_match
|
|
14
15
|
|
|
15
16
|
PathType = TypeVar("PathType", bound="Path")
|
|
16
17
|
|
|
17
|
-
# Long import times relative to usage frequency: lazy imports
|
|
18
|
-
# from datetime import datetime
|
|
19
|
-
# import dirhash
|
|
20
|
-
|
|
21
18
|
|
|
22
19
|
class Path(cached_content.Path):
|
|
23
20
|
"""
|
|
@@ -45,14 +42,16 @@ class Path(cached_content.Path):
|
|
|
45
42
|
return path
|
|
46
43
|
|
|
47
44
|
def with_timestamp(self: PathType) -> PathType:
|
|
48
|
-
from datetime import datetime
|
|
45
|
+
from datetime import datetime, timezone
|
|
49
46
|
|
|
50
|
-
timestamp =
|
|
51
|
-
|
|
47
|
+
timestamp = int(time.time()) # precision up to second
|
|
48
|
+
datetime_timestamp = datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
|
49
|
+
return self.with_stem(f"{self.stem} {datetime_timestamp}")
|
|
52
50
|
|
|
53
51
|
def copy_to(
|
|
54
52
|
self,
|
|
55
53
|
dest: PathType,
|
|
54
|
+
*,
|
|
56
55
|
include_properties: bool = True,
|
|
57
56
|
only_if_newer: bool = False,
|
|
58
57
|
) -> None:
|
|
@@ -70,18 +69,22 @@ class Path(cached_content.Path):
|
|
|
70
69
|
def archive_format(self) -> str:
|
|
71
70
|
# noinspection PyProtectedMember
|
|
72
71
|
path_str = str(self)
|
|
73
|
-
format_ = shutil._find_unpack_format(path_str) # type: ignore[attr-defined]
|
|
72
|
+
format_ = shutil._find_unpack_format(path_str) # type: ignore[attr-defined] # noqa: SLF001
|
|
74
73
|
return typing.cast(str, format_)
|
|
75
74
|
|
|
76
75
|
def unpack_if_archive(
|
|
77
|
-
self,
|
|
76
|
+
self,
|
|
77
|
+
*,
|
|
78
|
+
extraction_directory: PathType | None = None,
|
|
79
|
+
recursive: bool = True,
|
|
78
80
|
) -> None:
|
|
79
81
|
if self.archive_format is not None:
|
|
80
|
-
self.unpack(
|
|
82
|
+
self.unpack(extraction_directory, recursive=recursive)
|
|
81
83
|
|
|
82
|
-
def unpack(
|
|
84
|
+
def unpack( # noqa: PLR0913
|
|
83
85
|
self: PathType,
|
|
84
|
-
|
|
86
|
+
extraction_directory: PathType | None = None,
|
|
87
|
+
*,
|
|
85
88
|
remove_existing: bool = True,
|
|
86
89
|
preserve_properties: bool = True,
|
|
87
90
|
remove_original: bool = True,
|
|
@@ -94,43 +97,48 @@ class Path(cached_content.Path):
|
|
|
94
97
|
if subfolder.exists() and cleanup_path.number_of_children == 1:
|
|
95
98
|
subfolder.pop_parent() # pragma: nocover
|
|
96
99
|
|
|
97
|
-
def cast_path(casted_path: PathType | None) -> PathType:
|
|
98
|
-
return casted_path # type: ignore
|
|
99
|
-
|
|
100
100
|
if archive_format is None:
|
|
101
101
|
archive_format = self.archive_format
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
for archive_ext in unpack_info[0]:
|
|
109
|
-
if extract_name.endswith(archive_ext):
|
|
110
|
-
extract_name = extract_name.replace(archive_ext, "")
|
|
111
|
-
extract_dir = self.with_name(extract_name)
|
|
112
|
-
|
|
113
|
-
extract_dir = cast_path(extract_dir)
|
|
103
|
+
extraction_directory = (
|
|
104
|
+
self.create_extraction_directory(archive_format=archive_format)
|
|
105
|
+
if extraction_directory is None
|
|
106
|
+
else extraction_directory
|
|
107
|
+
)
|
|
114
108
|
|
|
115
109
|
if remove_existing:
|
|
116
|
-
if
|
|
117
|
-
|
|
110
|
+
if extraction_directory.is_dir():
|
|
111
|
+
extraction_directory.rmtree(missing_ok=True)
|
|
118
112
|
else:
|
|
119
|
-
|
|
113
|
+
extraction_directory.unlink(missing_ok=True)
|
|
120
114
|
|
|
121
|
-
shutil.unpack_archive(
|
|
115
|
+
shutil.unpack_archive(
|
|
116
|
+
self,
|
|
117
|
+
extract_dir=extraction_directory,
|
|
118
|
+
format=archive_format,
|
|
119
|
+
)
|
|
122
120
|
|
|
123
|
-
cleanup(
|
|
121
|
+
cleanup(extraction_directory)
|
|
124
122
|
if preserve_properties:
|
|
125
|
-
self.copy_properties_to(
|
|
123
|
+
self.copy_properties_to(extraction_directory)
|
|
126
124
|
|
|
127
125
|
if remove_original:
|
|
128
126
|
self.unlink()
|
|
129
127
|
|
|
130
128
|
if recursive:
|
|
131
|
-
for path in
|
|
129
|
+
for path in extraction_directory.find():
|
|
132
130
|
path.unpack_if_archive()
|
|
133
131
|
|
|
132
|
+
def create_extraction_directory(self: PathType, archive_format: str) -> PathType:
|
|
133
|
+
extract_name = self.name
|
|
134
|
+
# noinspection PyProtectedMember
|
|
135
|
+
unpack_formats = shutil._UNPACK_FORMATS # type: ignore[attr-defined] # noqa: SLF001
|
|
136
|
+
unpack_info = unpack_formats[archive_format]
|
|
137
|
+
for archive_ext in unpack_info[0]:
|
|
138
|
+
if extract_name.endswith(archive_ext):
|
|
139
|
+
extract_name = extract_name.replace(archive_ext, "")
|
|
140
|
+
return self.with_name(extract_name)
|
|
141
|
+
|
|
134
142
|
def pop_parent(self) -> None:
|
|
135
143
|
"""
|
|
136
144
|
Remove first parent from path in filesystem.
|
|
@@ -162,87 +170,85 @@ class Path(cached_content.Path):
|
|
|
162
170
|
This can be used to instantiate any object
|
|
163
171
|
:return: Content in path that contains yaml format
|
|
164
172
|
"""
|
|
165
|
-
import yaml #
|
|
173
|
+
import yaml # , autoimport
|
|
166
174
|
|
|
167
|
-
Loader: type[yaml.CFullLoader | yaml.FullLoader] = (
|
|
175
|
+
Loader: type[yaml.CFullLoader | yaml.FullLoader] = ( # noqa: N806
|
|
168
176
|
yaml.CFullLoader if hasattr(yaml, "CFullLoader") else yaml.FullLoader
|
|
169
177
|
)
|
|
170
|
-
return yaml.load(self.text, Loader=Loader) or {}
|
|
178
|
+
return yaml.load(self.text, Loader=Loader) or {} # noqa: S506
|
|
171
179
|
|
|
172
180
|
def update(self, value: dict[Any, Any]) -> dict[Any, Any]:
|
|
173
181
|
# only read and write if value to add not empty
|
|
174
182
|
if value:
|
|
175
|
-
current_content = self.yaml
|
|
176
|
-
assert isinstance(current_content, dict)
|
|
183
|
+
current_content = cast(dict[Any, Any], self.yaml)
|
|
177
184
|
updated_content = current_content | value
|
|
178
185
|
self.yaml = updated_content
|
|
179
186
|
else:
|
|
180
187
|
updated_content = value
|
|
181
188
|
return updated_content
|
|
182
189
|
|
|
183
|
-
def find(
|
|
190
|
+
def find( # noqa: PLR0913
|
|
184
191
|
self: PathType,
|
|
185
192
|
condition: Callable[[PathType], bool] | None = None,
|
|
186
|
-
exclude: Callable[[PathType], bool]
|
|
193
|
+
exclude: Callable[[PathType], bool] = lambda _: False,
|
|
194
|
+
*,
|
|
187
195
|
recurse_on_match: bool = False,
|
|
188
196
|
follow_symlinks: bool = False,
|
|
189
197
|
only_folders: bool = False,
|
|
190
|
-
) ->
|
|
198
|
+
) -> Iterator[PathType]:
|
|
191
199
|
"""Find all subpaths under path that match condition.
|
|
192
200
|
|
|
193
201
|
only_folders option can be used for efficiency reasons
|
|
194
202
|
"""
|
|
203
|
+
|
|
204
|
+
def extract_children_to_recurse_on(path: PathType) -> Iterator[PathType]:
|
|
205
|
+
# skip folders that do not allow listing
|
|
206
|
+
with contextlib.suppress(PermissionError):
|
|
207
|
+
for child in path.iterdir():
|
|
208
|
+
should_follow_symlink = follow_symlinks or not child.is_symlink()
|
|
209
|
+
should_follow_directories = not only_folders or child.is_dir()
|
|
210
|
+
if should_follow_symlink and should_follow_directories:
|
|
211
|
+
yield child
|
|
212
|
+
|
|
195
213
|
if condition is None:
|
|
196
214
|
recurse_on_match = True
|
|
197
215
|
|
|
198
216
|
def condition(_: PathType) -> bool:
|
|
199
217
|
return True
|
|
200
218
|
|
|
201
|
-
if exclude is None:
|
|
202
|
-
|
|
203
|
-
def exclude(_: PathType) -> bool:
|
|
204
|
-
return False
|
|
205
|
-
|
|
206
219
|
to_traverse = [self] if self.exists() else []
|
|
207
220
|
while to_traverse:
|
|
208
221
|
path = to_traverse.pop(0)
|
|
209
|
-
|
|
210
222
|
if not exclude(path):
|
|
211
223
|
match = condition(path)
|
|
212
224
|
if match:
|
|
213
225
|
yield path
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
for child in path.iterdir():
|
|
219
|
-
if follow_symlinks or not child.is_symlink():
|
|
220
|
-
if not only_folders or child.is_dir():
|
|
221
|
-
to_traverse.append(child)
|
|
222
|
-
except PermissionError: # pragma: nocover
|
|
223
|
-
# skip folders that do not allow listing
|
|
224
|
-
pass
|
|
226
|
+
should_recurse = recurse_on_match or not match
|
|
227
|
+
should_recurse_folder = only_folders or path.is_dir()
|
|
228
|
+
if should_recurse and should_recurse_folder:
|
|
229
|
+
to_traverse += list(extract_children_to_recurse_on(path))
|
|
225
230
|
|
|
226
231
|
def rmtree(
|
|
227
232
|
self,
|
|
233
|
+
*,
|
|
228
234
|
missing_ok: bool = False,
|
|
229
235
|
remove_root: bool = True,
|
|
230
236
|
ignore_errors: bool = False,
|
|
231
237
|
) -> None:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
context = (
|
|
239
|
+
contextlib.suppress(FileNotFoundError)
|
|
240
|
+
if missing_ok
|
|
241
|
+
else contextlib.nullcontext()
|
|
242
|
+
)
|
|
243
|
+
with context:
|
|
244
|
+
shutil.rmtree(self, ignore_errors=ignore_errors, onerror=self._on_error) # type: ignore[arg-type]
|
|
239
245
|
if not remove_root:
|
|
240
246
|
self.mkdir()
|
|
241
247
|
|
|
242
248
|
@classmethod
|
|
243
249
|
def _on_error(
|
|
244
250
|
cls,
|
|
245
|
-
_: bool,
|
|
251
|
+
_: bool, # noqa: FBT001
|
|
246
252
|
path_str: str,
|
|
247
253
|
exc_info: tuple[type[Exception], Exception, TracebackType],
|
|
248
254
|
) -> None:
|
|
@@ -258,7 +264,7 @@ class Path(cached_content.Path):
|
|
|
258
264
|
tokens_to_replace = self._flavour.sep, "."
|
|
259
265
|
for part in parts:
|
|
260
266
|
for token in tokens_to_replace:
|
|
261
|
-
part = part.replace(token, "_")
|
|
267
|
+
part = part.replace(token, "_") # noqa: PLW2901
|
|
262
268
|
path /= part
|
|
263
269
|
return path
|
|
264
270
|
|
|
@@ -269,7 +275,11 @@ class Path(cached_content.Path):
|
|
|
269
275
|
|
|
270
276
|
@classmethod
|
|
271
277
|
def tempfile(
|
|
272
|
-
cls: type[PathType],
|
|
278
|
+
cls: type[PathType],
|
|
279
|
+
*,
|
|
280
|
+
in_memory: bool = True,
|
|
281
|
+
create: bool = True,
|
|
282
|
+
**kwargs: Any,
|
|
273
283
|
) -> PathType:
|
|
274
284
|
"""Usage:
|
|
275
285
|
|
|
@@ -288,7 +298,7 @@ class Path(cached_content.Path):
|
|
|
288
298
|
return path
|
|
289
299
|
|
|
290
300
|
@classmethod
|
|
291
|
-
def tempdir(cls: type[PathType], in_memory: bool = True) -> PathType:
|
|
301
|
+
def tempdir(cls: type[PathType], *, in_memory: bool = True) -> PathType:
|
|
292
302
|
path = cls.tempfile(in_memory=in_memory, create=False)
|
|
293
303
|
path.mkdir()
|
|
294
304
|
return path
|
|
@@ -296,7 +306,7 @@ class Path(cached_content.Path):
|
|
|
296
306
|
def __enter__(self: PathType) -> PathType:
|
|
297
307
|
return self
|
|
298
308
|
|
|
299
|
-
def __exit__(self, *_:
|
|
309
|
+
def __exit__(self, *_: object) -> None:
|
|
300
310
|
if self.is_file():
|
|
301
311
|
self.unlink(missing_ok=True)
|
|
302
312
|
else:
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import hashlib
|
|
2
3
|
import mimetypes
|
|
3
4
|
import os
|
|
4
5
|
import subprocess
|
|
5
|
-
import typing
|
|
6
6
|
import warnings
|
|
7
7
|
from collections.abc import Callable
|
|
8
8
|
from functools import wraps
|
|
9
|
-
from typing import Any, TypeVar
|
|
9
|
+
from typing import Any, TypeVar, cast
|
|
10
10
|
|
|
11
11
|
from . import content_properties
|
|
12
12
|
|
|
@@ -45,20 +45,20 @@ class Path(content_properties.Path):
|
|
|
45
45
|
os.utime(self, (time, time)) # set create time as well
|
|
46
46
|
|
|
47
47
|
command = "touch", "-d", f"@{time}", self
|
|
48
|
-
|
|
49
|
-
subprocess.
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
with contextlib.suppress(
|
|
49
|
+
subprocess.CalledProcessError,
|
|
50
|
+
): # Doesn't work on Windows
|
|
51
|
+
subprocess.run(command, check=False) # noqa: S603
|
|
52
52
|
|
|
53
53
|
@property
|
|
54
54
|
def tags(self) -> list[str]:
|
|
55
|
-
from .tags import XDGTags #
|
|
55
|
+
from .tags import XDGTags # , autoimport
|
|
56
56
|
|
|
57
57
|
return XDGTags(self).get()
|
|
58
58
|
|
|
59
59
|
@tags.setter
|
|
60
60
|
def tags(self, values: list[str | int | None]) -> None:
|
|
61
|
-
from .tags import XDGTags #
|
|
61
|
+
from .tags import XDGTags # , autoimport
|
|
62
62
|
|
|
63
63
|
if len(values) == 0:
|
|
64
64
|
XDGTags(self).clear()
|
|
@@ -71,7 +71,7 @@ class Path(content_properties.Path):
|
|
|
71
71
|
|
|
72
72
|
@tag.setter
|
|
73
73
|
def tag(self, value: str | int | None) -> None:
|
|
74
|
-
from .tags import XDGTags #
|
|
74
|
+
from .tags import XDGTags # , autoimport
|
|
75
75
|
|
|
76
76
|
XDGTags(self).set(value)
|
|
77
77
|
|
|
@@ -110,15 +110,16 @@ class Path(content_properties.Path):
|
|
|
110
110
|
def dir_content_hash(self) -> str | None:
|
|
111
111
|
# dirhash package throws annoying warnings
|
|
112
112
|
warnings.filterwarnings(
|
|
113
|
-
action="ignore",
|
|
113
|
+
action="ignore",
|
|
114
|
+
module="pkg_resources|dirhash",
|
|
115
|
+
category=DeprecationWarning,
|
|
114
116
|
)
|
|
115
117
|
|
|
116
|
-
import dirhash #
|
|
118
|
+
import dirhash # , autoimport
|
|
117
119
|
|
|
118
120
|
# use default algorithm used in cloud provider checksums
|
|
119
121
|
# can be efficient because not used for cryptographic security
|
|
120
|
-
|
|
121
|
-
return typing.cast(str, content_hash)
|
|
122
|
+
return cast(str, dirhash.dirhash(self, "md5")) if self.has_children else None
|
|
122
123
|
|
|
123
124
|
@property
|
|
124
125
|
def file_content_hash(self) -> str:
|
|
@@ -33,7 +33,11 @@ class Path(encryption.Path):
|
|
|
33
33
|
|
|
34
34
|
@create_parent_on_missing
|
|
35
35
|
def touch(
|
|
36
|
-
self,
|
|
36
|
+
self,
|
|
37
|
+
mode: int = 0o666,
|
|
38
|
+
*,
|
|
39
|
+
exist_ok: bool = True,
|
|
40
|
+
mtime: float | None = None,
|
|
37
41
|
) -> None:
|
|
38
42
|
super().touch(mode=mode, exist_ok=exist_ok)
|
|
39
43
|
if mtime is not None:
|
|
@@ -43,22 +47,22 @@ class Path(encryption.Path):
|
|
|
43
47
|
def rmdir(self) -> None:
|
|
44
48
|
return super().rmdir()
|
|
45
49
|
|
|
46
|
-
def iterdir(self: T, missing_ok: bool = True) -> Generator[T, None, None]:
|
|
50
|
+
def iterdir(self: T, *, missing_ok: bool = True) -> Generator[T, None, None]:
|
|
47
51
|
if self.exists() or not missing_ok:
|
|
48
52
|
yield from super().iterdir()
|
|
49
53
|
|
|
50
54
|
@create_parent_on_missing
|
|
51
|
-
def rename(self: T, target: str | T, exist_ok: bool = False) -> T:
|
|
55
|
+
def rename(self: T, target: str | T, *, exist_ok: bool = False) -> T:
|
|
52
56
|
target_path = self.__class__(target)
|
|
53
57
|
rename = super().replace if exist_ok else super().rename
|
|
54
58
|
try:
|
|
55
59
|
target_path.create_parent()
|
|
56
60
|
target_path = rename(target_path)
|
|
57
|
-
except OSError as
|
|
58
|
-
if exist_ok and "Directory not empty" in str(
|
|
61
|
+
except OSError as exception:
|
|
62
|
+
if exist_ok and "Directory not empty" in str(exception):
|
|
59
63
|
target_path.rmtree()
|
|
60
64
|
target_path = rename(target_path)
|
|
61
|
-
elif "Invalid cross-device link" in str(
|
|
65
|
+
elif "Invalid cross-device link" in str(exception):
|
|
62
66
|
# target is on different file system
|
|
63
67
|
if target_path.exists():
|
|
64
68
|
if exist_ok:
|
|
@@ -68,7 +72,7 @@ class Path(encryption.Path):
|
|
|
68
72
|
target_path.unlink() # pragma: nocover
|
|
69
73
|
else:
|
|
70
74
|
message = f"Target already exists: {target_path }"
|
|
71
|
-
raise
|
|
75
|
+
raise RuntimeError(message) from exception
|
|
72
76
|
else:
|
|
73
77
|
target_path.create_parent()
|
|
74
78
|
target_path = shutil.move(self, target_path)
|
|
@@ -80,7 +84,7 @@ class Path(encryption.Path):
|
|
|
80
84
|
path = self.rename(target, exist_ok=True)
|
|
81
85
|
return typing.cast(T, path)
|
|
82
86
|
|
|
83
|
-
def open(self, mode: str = "r", **kwargs: Any) -> IO[Any]: # type: ignore
|
|
87
|
+
def open(self, mode: str = "r", **kwargs: Any) -> IO[Any]: # type: ignore[override]
|
|
84
88
|
try:
|
|
85
89
|
res = super().open(mode, **kwargs)
|
|
86
90
|
except FileNotFoundError:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: superpathlib
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Extended Pathlib
|
|
5
5
|
Author-email: Quinten Roets <qdr2104@columbia.edu>
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ License-File: LICENSE
|
|
|
11
11
|
Requires-Dist: simple-classproperty<5,>=4.0.2
|
|
12
12
|
Provides-Extra: full
|
|
13
13
|
Requires-Dist: dirhash<1,>=0.2.1; extra == "full"
|
|
14
|
-
Requires-Dist: numpy<
|
|
14
|
+
Requires-Dist: numpy<3,>=1.26.4; extra == "full"
|
|
15
15
|
Requires-Dist: package-utils<1,>=0.6.1; extra == "full"
|
|
16
16
|
Requires-Dist: PyYaml<7,>=6.0.1; extra == "full"
|
|
17
17
|
Requires-Dist: xattr<2,>=0.10.1; extra == "full"
|
|
@@ -20,7 +20,7 @@ Requires-Dist: hypothesis<7,>=6.97.1; extra == "dev"
|
|
|
20
20
|
Requires-Dist: package-dev-tools<1,>=0.5.12; extra == "dev"
|
|
21
21
|
Requires-Dist: types-PyYaml<7,>=6.0.12.12; extra == "dev"
|
|
22
22
|
Requires-Dist: dirhash<1,>=0.2.1; extra == "dev"
|
|
23
|
-
Requires-Dist: numpy<
|
|
23
|
+
Requires-Dist: numpy<3,>=1.26.4; extra == "dev"
|
|
24
24
|
Requires-Dist: package-utils<1,>=0.6.1; extra == "dev"
|
|
25
25
|
Requires-Dist: PyYaml<7,>=6.0.1; extra == "dev"
|
|
26
26
|
Requires-Dist: xattr<2,>=0.10.1; extra == "dev"
|
|
@@ -5,14 +5,14 @@ hypothesis<7,>=6.97.1
|
|
|
5
5
|
package-dev-tools<1,>=0.5.12
|
|
6
6
|
types-PyYaml<7,>=6.0.12.12
|
|
7
7
|
dirhash<1,>=0.2.1
|
|
8
|
-
numpy<
|
|
8
|
+
numpy<3,>=1.26.4
|
|
9
9
|
package-utils<1,>=0.6.1
|
|
10
10
|
PyYaml<7,>=6.0.1
|
|
11
11
|
xattr<2,>=0.10.1
|
|
12
12
|
|
|
13
13
|
[full]
|
|
14
14
|
dirhash<1,>=0.2.1
|
|
15
|
-
numpy<
|
|
15
|
+
numpy<3,>=1.26.4
|
|
16
16
|
package-utils<1,>=0.6.1
|
|
17
17
|
PyYaml<7,>=6.0.1
|
|
18
18
|
xattr<2,>=0.10.1
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from content import byte_content, text_content
|
|
5
4
|
from package_utils.storage import CachedFileContent
|
|
6
5
|
from superpathlib import Path
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
from tests.content import byte_content, text_content
|
|
8
|
+
from tests.utils import ignore_fixture_warning
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@ignore_fixture_warning
|
|
@@ -31,7 +32,8 @@ def test_text(content: str) -> None:
|
|
|
31
32
|
def test_content(path: Path, content: dict[str, dict[str, str]]) -> None:
|
|
32
33
|
class Storage:
|
|
33
34
|
content: CachedFileContent[dict[str, dict[str, str]]] = typing.cast(
|
|
34
|
-
CachedFileContent[dict[str, dict[str, str]]],
|
|
35
|
+
CachedFileContent[dict[str, dict[str, str]]],
|
|
36
|
+
path.cached_content,
|
|
35
37
|
)
|
|
36
38
|
|
|
37
39
|
verify_storage(Storage, content)
|
|
@@ -42,14 +44,16 @@ def test_content(path: Path, content: dict[str, dict[str, str]]) -> None:
|
|
|
42
44
|
def test_created_content(path: Path, content: dict[str, dict[str, str]]) -> None:
|
|
43
45
|
class Storage:
|
|
44
46
|
content: CachedFileContent[dict[str, dict[str, str]]] = typing.cast(
|
|
45
|
-
CachedFileContent[dict[str, dict[str, str]]],
|
|
47
|
+
CachedFileContent[dict[str, dict[str, str]]],
|
|
48
|
+
path.create_cached_content({}),
|
|
46
49
|
)
|
|
47
50
|
|
|
48
51
|
verify_storage(Storage, content)
|
|
49
52
|
|
|
50
53
|
|
|
51
54
|
def verify_storage(
|
|
52
|
-
storage_class: type[Any],
|
|
55
|
+
storage_class: type[Any],
|
|
56
|
+
content: str | bytes | dict[str, dict[str, str]],
|
|
53
57
|
) -> None:
|
|
54
58
|
storage = storage_class()
|
|
55
59
|
assert storage.content is not None
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
from
|
|
2
|
+
from superpathlib import Path
|
|
3
|
+
|
|
4
|
+
from tests.content import (
|
|
3
5
|
byte_content,
|
|
4
6
|
dictionary_content,
|
|
5
7
|
floats_content,
|
|
@@ -7,8 +9,7 @@ from content import (
|
|
|
7
9
|
text_content,
|
|
8
10
|
text_lines_content,
|
|
9
11
|
)
|
|
10
|
-
from
|
|
11
|
-
from utils import ignore_fixture_warning
|
|
12
|
+
from tests.utils import ignore_fixture_warning
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@ignore_fixture_warning
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
-
from
|
|
4
|
+
from superpathlib import Path
|
|
5
|
+
|
|
6
|
+
from tests.content import (
|
|
5
7
|
byte_content,
|
|
6
8
|
dictionary_content,
|
|
7
9
|
slower_test_settings,
|
|
8
10
|
text_lines_content,
|
|
9
11
|
)
|
|
10
|
-
from
|
|
11
|
-
from utils import ignore_fixture_warning
|
|
12
|
+
from tests.utils import ignore_fixture_warning
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def test_tempfile() -> None:
|
|
@@ -47,7 +48,7 @@ def test_unpack_check(directory: Path) -> None:
|
|
|
47
48
|
non_archive_assets = Path(__file__).parent / "assets" / "non_archives"
|
|
48
49
|
assert not non_archive_assets.is_empty()
|
|
49
50
|
for path in non_archive_assets.iterdir():
|
|
50
|
-
path.unpack_if_archive(directory)
|
|
51
|
+
path.unpack_if_archive(extraction_directory=directory)
|
|
51
52
|
assert directory.is_empty()
|
|
52
53
|
|
|
53
54
|
|
|
@@ -103,7 +104,9 @@ def test_move_existing(path: Path, path2: Path, content: bytes) -> None:
|
|
|
103
104
|
@ignore_fixture_warning
|
|
104
105
|
@byte_content
|
|
105
106
|
def test_move_parent_not_existing(
|
|
106
|
-
directory: Path,
|
|
107
|
+
directory: Path,
|
|
108
|
+
directory2: Path,
|
|
109
|
+
content: bytes,
|
|
107
110
|
) -> None:
|
|
108
111
|
directory.rmtree()
|
|
109
112
|
path = directory / directory.name
|
|
@@ -133,7 +136,9 @@ def test_move_directory(directory: Path, directory2: Path, content: bytes) -> No
|
|
|
133
136
|
@ignore_fixture_warning
|
|
134
137
|
@byte_content
|
|
135
138
|
def test_move_directory_existing(
|
|
136
|
-
directory: Path,
|
|
139
|
+
directory: Path,
|
|
140
|
+
directory2: Path,
|
|
141
|
+
content: bytes,
|
|
137
142
|
) -> None:
|
|
138
143
|
def move_function() -> None:
|
|
139
144
|
directory.rename(directory2, exist_ok=True)
|
|
@@ -144,7 +149,9 @@ def test_move_directory_existing(
|
|
|
144
149
|
@ignore_fixture_warning
|
|
145
150
|
@byte_content
|
|
146
151
|
def test_replace_directory_existing(
|
|
147
|
-
directory: Path,
|
|
152
|
+
directory: Path,
|
|
153
|
+
directory2: Path,
|
|
154
|
+
content: bytes,
|
|
148
155
|
) -> None:
|
|
149
156
|
def move_function() -> None:
|
|
150
157
|
directory.replace(directory2)
|
|
@@ -155,7 +162,9 @@ def test_replace_directory_existing(
|
|
|
155
162
|
@ignore_fixture_warning
|
|
156
163
|
@byte_content
|
|
157
164
|
def test_move_directory_different_filesystem(
|
|
158
|
-
directory: Path,
|
|
165
|
+
directory: Path,
|
|
166
|
+
in_memory_directory: Path,
|
|
167
|
+
content: bytes,
|
|
159
168
|
) -> None:
|
|
160
169
|
filename = directory.name
|
|
161
170
|
subpath = directory / filename
|
|
@@ -173,7 +182,9 @@ def test_move_directory_different_filesystem(
|
|
|
173
182
|
@ignore_fixture_warning
|
|
174
183
|
@byte_content
|
|
175
184
|
def test_move_directory_existing_different_filesystem(
|
|
176
|
-
directory: Path,
|
|
185
|
+
directory: Path,
|
|
186
|
+
in_memory_directory: Path,
|
|
187
|
+
content: bytes,
|
|
177
188
|
) -> None:
|
|
178
189
|
def move_function() -> None:
|
|
179
190
|
directory.rename(in_memory_directory, exist_ok=True)
|
|
@@ -2,11 +2,12 @@ import hashlib
|
|
|
2
2
|
import math
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
|
-
from content import byte_content, text_strategy
|
|
6
5
|
from hypothesis import given, strategies
|
|
7
6
|
from hypothesis.strategies import lists
|
|
8
7
|
from superpathlib import Path
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
from tests.content import byte_content, text_strategy
|
|
10
|
+
from tests.utils import ignore_fixture_warning
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@ignore_fixture_warning
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|