kaparoo-python 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- kaparoo/filesystem/__init__.py +15 -0
- kaparoo/filesystem/directory.py +81 -2
- kaparoo/filesystem/search/classes.py +1 -1
- kaparoo/filesystem/staged.py +458 -0
- kaparoo/filesystem/utils.py +144 -1
- kaparoo/utils/__init__.py +23 -3
- kaparoo/utils/aggregate.py +342 -0
- kaparoo/utils/timer.py +146 -53
- {kaparoo_python-0.3.0.dist-info → kaparoo_python-0.4.0.dist-info}/METADATA +9 -5
- {kaparoo_python-0.3.0.dist-info → kaparoo_python-0.4.0.dist-info}/RECORD +12 -10
- {kaparoo_python-0.3.0.dist-info → kaparoo_python-0.4.0.dist-info}/WHEEL +1 -1
- {kaparoo_python-0.3.0.dist-info → kaparoo_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
kaparoo/filesystem/__init__.py
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
__all__ = (
|
|
2
2
|
"DirectoryNotFoundError",
|
|
3
3
|
"NotAFileError",
|
|
4
|
+
"StagedDirectory",
|
|
5
|
+
"StagedFile",
|
|
4
6
|
"dir_empty",
|
|
5
7
|
"dir_empty_unsafe",
|
|
6
8
|
"dir_exists",
|
|
9
|
+
"dir_not_empty",
|
|
10
|
+
"dir_not_empty_unsafe",
|
|
7
11
|
"dirs_empty",
|
|
8
12
|
"dirs_empty_unsafe",
|
|
9
13
|
"dirs_exist",
|
|
14
|
+
"dirs_not_empty",
|
|
15
|
+
"dirs_not_empty_unsafe",
|
|
10
16
|
"ensure_dir_exists",
|
|
11
17
|
"ensure_dirs_exist",
|
|
12
18
|
"ensure_file_exists",
|
|
@@ -22,6 +28,8 @@ __all__ = (
|
|
|
22
28
|
"make_dirs",
|
|
23
29
|
"path_exists",
|
|
24
30
|
"paths_exist",
|
|
31
|
+
"reserve_path",
|
|
32
|
+
"reserve_paths",
|
|
25
33
|
"search_dirs",
|
|
26
34
|
"search_files",
|
|
27
35
|
"search_paths",
|
|
@@ -34,8 +42,12 @@ __all__ = (
|
|
|
34
42
|
from kaparoo.filesystem.directory import (
|
|
35
43
|
dir_empty,
|
|
36
44
|
dir_empty_unsafe,
|
|
45
|
+
dir_not_empty,
|
|
46
|
+
dir_not_empty_unsafe,
|
|
37
47
|
dirs_empty,
|
|
38
48
|
dirs_empty_unsafe,
|
|
49
|
+
dirs_not_empty,
|
|
50
|
+
dirs_not_empty_unsafe,
|
|
39
51
|
make_dir,
|
|
40
52
|
make_dirs,
|
|
41
53
|
)
|
|
@@ -65,7 +77,10 @@ from kaparoo.filesystem.search import (
|
|
|
65
77
|
search_files,
|
|
66
78
|
search_paths,
|
|
67
79
|
)
|
|
80
|
+
from kaparoo.filesystem.staged import StagedDirectory, StagedFile
|
|
68
81
|
from kaparoo.filesystem.utils import (
|
|
82
|
+
reserve_path,
|
|
83
|
+
reserve_paths,
|
|
69
84
|
stringify_path,
|
|
70
85
|
stringify_paths,
|
|
71
86
|
wrap_path,
|
kaparoo/filesystem/directory.py
CHANGED
|
@@ -3,13 +3,18 @@ from __future__ import annotations
|
|
|
3
3
|
__all__ = (
|
|
4
4
|
"dir_empty",
|
|
5
5
|
"dir_empty_unsafe",
|
|
6
|
+
"dir_not_empty",
|
|
7
|
+
"dir_not_empty_unsafe",
|
|
6
8
|
"dirs_empty",
|
|
7
9
|
"dirs_empty_unsafe",
|
|
10
|
+
"dirs_not_empty",
|
|
11
|
+
"dirs_not_empty_unsafe",
|
|
8
12
|
"make_dir",
|
|
9
13
|
"make_dirs",
|
|
10
14
|
)
|
|
11
15
|
|
|
12
16
|
import os
|
|
17
|
+
import shutil
|
|
13
18
|
from pathlib import Path
|
|
14
19
|
from typing import TYPE_CHECKING, overload
|
|
15
20
|
|
|
@@ -39,6 +44,7 @@ def make_dir(
|
|
|
39
44
|
*,
|
|
40
45
|
mode: int = 0o777,
|
|
41
46
|
exist_ok: bool = False,
|
|
47
|
+
clean: bool = False,
|
|
42
48
|
stringify: Literal[False] = False,
|
|
43
49
|
) -> Path: ...
|
|
44
50
|
|
|
@@ -49,6 +55,7 @@ def make_dir(
|
|
|
49
55
|
*,
|
|
50
56
|
mode: int = 0o777,
|
|
51
57
|
exist_ok: bool = False,
|
|
58
|
+
clean: bool = False,
|
|
52
59
|
stringify: Literal[True],
|
|
53
60
|
) -> str: ...
|
|
54
61
|
|
|
@@ -59,6 +66,7 @@ def make_dir(
|
|
|
59
66
|
*,
|
|
60
67
|
mode: int = 0o777,
|
|
61
68
|
exist_ok: bool = False,
|
|
69
|
+
clean: bool = False,
|
|
62
70
|
stringify: bool,
|
|
63
71
|
) -> Path | str: ...
|
|
64
72
|
|
|
@@ -68,6 +76,7 @@ def make_dir(
|
|
|
68
76
|
*,
|
|
69
77
|
mode: int = 0o777,
|
|
70
78
|
exist_ok: bool = False,
|
|
79
|
+
clean: bool = False,
|
|
71
80
|
stringify: bool = False,
|
|
72
81
|
) -> Path | str:
|
|
73
82
|
"""Recursively create a directory.
|
|
@@ -77,6 +86,11 @@ def make_dir(
|
|
|
77
86
|
mode: The mode to use when creating the directory. Defaults to 0o777.
|
|
78
87
|
exist_ok: Whether to suppress OSError if the path already exists.
|
|
79
88
|
Defaults to False.
|
|
89
|
+
clean: Whether to recreate the directory empty when it already exists,
|
|
90
|
+
removing its contents first. Only an existing *directory* is wiped;
|
|
91
|
+
a non-directory still raises. Because the directory is removed and
|
|
92
|
+
remade, `clean=True` makes `exist_ok` moot. **Destructive.**
|
|
93
|
+
Defaults to False.
|
|
80
94
|
stringify: Whether to return the path as a string. Defaults to False.
|
|
81
95
|
|
|
82
96
|
Returns:
|
|
@@ -87,13 +101,16 @@ def make_dir(
|
|
|
87
101
|
ValueError: If `mode` is outside the range 0o1-0o7777
|
|
88
102
|
(not checked on Windows, where the mode is ignored).
|
|
89
103
|
NotADirectoryError: If the path exists but is not a directory.
|
|
90
|
-
OSError: If `exist_ok` is False and the path
|
|
104
|
+
OSError: If `exist_ok` is False, `clean` is False, and the path
|
|
105
|
+
already exists.
|
|
91
106
|
"""
|
|
92
107
|
_validate_mode(mode)
|
|
93
108
|
path = Path(path)
|
|
94
109
|
if path.exists() and not path.is_dir():
|
|
95
110
|
msg = f"not a directory: {path}"
|
|
96
111
|
raise NotADirectoryError(msg)
|
|
112
|
+
if clean and path.is_dir():
|
|
113
|
+
shutil.rmtree(path)
|
|
97
114
|
path.mkdir(mode=mode, parents=True, exist_ok=exist_ok)
|
|
98
115
|
return stringify_path(path) if stringify else path
|
|
99
116
|
|
|
@@ -105,6 +122,7 @@ def make_dirs(
|
|
|
105
122
|
root: StrPath | None = None,
|
|
106
123
|
mode: int = 0o777,
|
|
107
124
|
exist_ok: bool = False,
|
|
125
|
+
clean: bool = False,
|
|
108
126
|
stringify: Literal[False] = False,
|
|
109
127
|
) -> Sequence[Path]: ...
|
|
110
128
|
|
|
@@ -116,6 +134,7 @@ def make_dirs(
|
|
|
116
134
|
root: StrPath | None = None,
|
|
117
135
|
mode: int = 0o777,
|
|
118
136
|
exist_ok: bool = False,
|
|
137
|
+
clean: bool = False,
|
|
119
138
|
stringify: Literal[True],
|
|
120
139
|
) -> Sequence[str]: ...
|
|
121
140
|
|
|
@@ -127,6 +146,7 @@ def make_dirs(
|
|
|
127
146
|
root: StrPath | None = None,
|
|
128
147
|
mode: int = 0o777,
|
|
129
148
|
exist_ok: bool = False,
|
|
149
|
+
clean: bool = False,
|
|
130
150
|
stringify: bool,
|
|
131
151
|
) -> Sequence[Path] | Sequence[str]: ...
|
|
132
152
|
|
|
@@ -137,6 +157,7 @@ def make_dirs(
|
|
|
137
157
|
root: StrPath | None = None,
|
|
138
158
|
mode: int = 0o777,
|
|
139
159
|
exist_ok: bool = False,
|
|
160
|
+
clean: bool = False,
|
|
140
161
|
stringify: bool = False,
|
|
141
162
|
) -> Sequence[Path] | Sequence[str]:
|
|
142
163
|
"""Recursively create directories.
|
|
@@ -147,6 +168,11 @@ def make_dirs(
|
|
|
147
168
|
mode: The mode to use when creating the directories. Defaults to 0o777.
|
|
148
169
|
exist_ok: Whether to suppress OSError if any of the paths already exist.
|
|
149
170
|
Defaults to False.
|
|
171
|
+
clean: Whether to recreate each directory empty when it already exists,
|
|
172
|
+
removing its contents first. Only an existing *directory* is wiped;
|
|
173
|
+
a non-directory still raises. Because the directory is removed and
|
|
174
|
+
remade, `clean=True` makes `exist_ok` moot. **Destructive.**
|
|
175
|
+
Defaults to False.
|
|
150
176
|
stringify: Whether to return the paths as strings. Defaults to False.
|
|
151
177
|
|
|
152
178
|
Returns:
|
|
@@ -159,13 +185,16 @@ def make_dirs(
|
|
|
159
185
|
DirectoryNotFoundError: If `root` is provided and does not exist.
|
|
160
186
|
NotADirectoryError: If `root` is provided and is not a directory.
|
|
161
187
|
ValueError: If `root` is provided and any of the paths are absolute.
|
|
162
|
-
OSError: If `exist_ok` is False and any of the
|
|
188
|
+
OSError: If `exist_ok` is False, `clean` is False, and any of the
|
|
189
|
+
paths already exist.
|
|
163
190
|
OSError: If any of the paths are not directories.
|
|
164
191
|
"""
|
|
165
192
|
_validate_mode(mode)
|
|
166
193
|
paths = _join_root_if_provided(paths, root)
|
|
167
194
|
directories = [Path(p) for p in paths]
|
|
168
195
|
for directory in directories:
|
|
196
|
+
if clean and directory.is_dir():
|
|
197
|
+
shutil.rmtree(directory)
|
|
169
198
|
directory.mkdir(mode=mode, parents=True, exist_ok=exist_ok)
|
|
170
199
|
return stringify_paths(directories) if stringify else directories
|
|
171
200
|
|
|
@@ -224,3 +253,53 @@ def dirs_empty(paths: StrPaths, *, root: StrPath | None = None) -> bool:
|
|
|
224
253
|
"""
|
|
225
254
|
paths = ensure_dirs_exist(paths, root=root)
|
|
226
255
|
return all(dir_empty_unsafe(p) for p in paths)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def dir_not_empty_unsafe(path: StrPath) -> bool:
|
|
259
|
+
"""Check if a directory is not empty without existence checks."""
|
|
260
|
+
return not dir_empty_unsafe(path)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def dirs_not_empty_unsafe(paths: StrPaths, *, root: StrPath | None = None) -> bool:
|
|
264
|
+
"""Check if directories are not empty without existence checks."""
|
|
265
|
+
if root is not None:
|
|
266
|
+
paths = [Path(root) / p for p in paths]
|
|
267
|
+
return all(dir_not_empty_unsafe(p) for p in paths)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def dir_not_empty(path: StrPath) -> bool:
|
|
271
|
+
"""Check if a directory is not empty.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
path: The directory path to check.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
True if the directory is not empty, False otherwise.
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
DirectoryNotFoundError: If the path does not exist.
|
|
281
|
+
NotADirectoryError: If the path is not a directory.
|
|
282
|
+
"""
|
|
283
|
+
path = ensure_dir_exists(path)
|
|
284
|
+
return dir_not_empty_unsafe(path)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def dirs_not_empty(paths: StrPaths, *, root: StrPath | None = None) -> bool:
|
|
288
|
+
"""Check if directories are not empty.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
paths: A sequence of directory paths to check.
|
|
292
|
+
root: The root directory to prepend to each path. Defaults to None.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
True if all directories are not empty, False otherwise.
|
|
296
|
+
|
|
297
|
+
Raises:
|
|
298
|
+
DirectoryNotFoundError: If `root` is provided and does not exist.
|
|
299
|
+
DirectoryNotFoundError: If any of the paths do not exist.
|
|
300
|
+
NotADirectoryError: If `root` is provided and is not a directory.
|
|
301
|
+
NotADirectoryError: If any of the paths are not directories.
|
|
302
|
+
ValueError: If `root` is provided and any of the paths are absolute.
|
|
303
|
+
"""
|
|
304
|
+
paths = ensure_dirs_exist(paths, root=root)
|
|
305
|
+
return all(dir_not_empty_unsafe(p) for p in paths)
|
|
@@ -169,7 +169,7 @@ class Search(ABC):
|
|
|
169
169
|
|
|
170
170
|
for dirpath, dirnames, filenames in root.walk():
|
|
171
171
|
child_depth = len(dirpath.parts) - root_depth + 1
|
|
172
|
-
part = stringify_path(dirpath
|
|
172
|
+
part = stringify_path(dirpath, after=root)
|
|
173
173
|
|
|
174
174
|
if child_depth >= min_depth and cls._filter_part(part, part_filter):
|
|
175
175
|
names = cls._select_names(dirnames, filenames)
|