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.
@@ -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,
@@ -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 already exists.
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 paths already exist.
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.relative_to(root))
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)