pythonwrench 0.6.4__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.
- pythonwrench/__init__.py +490 -0
- pythonwrench/__main__.py +7 -0
- pythonwrench/_core.py +192 -0
- pythonwrench/abc.py +30 -0
- pythonwrench/argparse/__init__.py +81 -0
- pythonwrench/argparse/dataclass_.py +284 -0
- pythonwrench/argparse/parsers.py +619 -0
- pythonwrench/cast.py +247 -0
- pythonwrench/checksum.py +427 -0
- pythonwrench/collections/__init__.py +104 -0
- pythonwrench/collections/collections.py +900 -0
- pythonwrench/collections/prop.py +104 -0
- pythonwrench/collections/reducers.py +330 -0
- pythonwrench/concurrent.py +73 -0
- pythonwrench/csv.py +12 -0
- pythonwrench/dataclasses.py +117 -0
- pythonwrench/datetime.py +17 -0
- pythonwrench/difflib.py +39 -0
- pythonwrench/disk_cache.py +615 -0
- pythonwrench/entrypoints/info.py +44 -0
- pythonwrench/entrypoints/safe_rmdir.py +98 -0
- pythonwrench/entrypoints/tree.py +113 -0
- pythonwrench/enum.py +55 -0
- pythonwrench/functools.py +234 -0
- pythonwrench/hashlib.py +95 -0
- pythonwrench/importlib.py +243 -0
- pythonwrench/inspect.py +69 -0
- pythonwrench/json.py +12 -0
- pythonwrench/jsonl.py +12 -0
- pythonwrench/logging.py +252 -0
- pythonwrench/math.py +107 -0
- pythonwrench/os.py +226 -0
- pythonwrench/pickle.py +12 -0
- pythonwrench/random.py +60 -0
- pythonwrench/re.py +139 -0
- pythonwrench/semver.py +406 -0
- pythonwrench/serialization/__init__.py +70 -0
- pythonwrench/serialization/_core.py +70 -0
- pythonwrench/serialization/csv.py +493 -0
- pythonwrench/serialization/json.py +178 -0
- pythonwrench/serialization/jsonl.py +215 -0
- pythonwrench/serialization/pickle.py +186 -0
- pythonwrench/time.py +34 -0
- pythonwrench/typing/__init__.py +125 -0
- pythonwrench/typing/checks.py +551 -0
- pythonwrench/typing/classes.py +251 -0
- pythonwrench/warnings.py +118 -0
- pythonwrench-0.6.4.dist-info/METADATA +242 -0
- pythonwrench-0.6.4.dist-info/RECORD +52 -0
- pythonwrench-0.6.4.dist-info/WHEEL +4 -0
- pythonwrench-0.6.4.dist-info/entry_points.txt +10 -0
- pythonwrench-0.6.4.dist-info/licenses/LICENSE +21 -0
pythonwrench/semver.py
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
from dataclasses import asdict, dataclass
|
|
8
|
+
from typing import Any, List, Mapping, Tuple, TypedDict, Union, overload
|
|
9
|
+
|
|
10
|
+
from typing_extensions import NotRequired, Self, TypeAlias
|
|
11
|
+
|
|
12
|
+
from pythonwrench.typing import NoneType, isinstance_generic
|
|
13
|
+
|
|
14
|
+
PreRelease: TypeAlias = Union[int, str, None, List[Union[int, str]]]
|
|
15
|
+
BuildMetadata: TypeAlias = Union[int, str, None, List[Union[int, str]]]
|
|
16
|
+
|
|
17
|
+
# Pattern of https://semver.org/
|
|
18
|
+
_VERSION_PATTERN = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
|
19
|
+
_VERSION_FORMAT = r"{major}.{minor}.{patch}"
|
|
20
|
+
_VERSION_KEYS = ("major", "minor", "patch", "prerelease", "buildmetadata")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class VersionDict(TypedDict):
|
|
27
|
+
"""TypedDict which represents a Version."""
|
|
28
|
+
|
|
29
|
+
major: int
|
|
30
|
+
minor: int
|
|
31
|
+
patch: int
|
|
32
|
+
prerelease: NotRequired[PreRelease]
|
|
33
|
+
buildmetadata: NotRequired[BuildMetadata]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
VersionTuple: TypeAlias = Union[
|
|
37
|
+
Tuple[int, int, int],
|
|
38
|
+
Tuple[int, int, int, PreRelease],
|
|
39
|
+
Tuple[int, int, int, PreRelease, BuildMetadata],
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
VersionDictLike: TypeAlias = Mapping[str, Union[int, PreRelease, BuildMetadata]]
|
|
43
|
+
VersionTupleLike: TypeAlias = Tuple[Union[int, PreRelease, BuildMetadata], ...]
|
|
44
|
+
VersionLike: TypeAlias = Union["Version", str, VersionDictLike, VersionTupleLike]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(init=False, eq=False)
|
|
48
|
+
class Version:
|
|
49
|
+
"""Version utility class following Semantic Versioning (SemVer) spec.
|
|
50
|
+
|
|
51
|
+
Version format is: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]
|
|
52
|
+
|
|
53
|
+
Based on https://semver.org/ version 2.0.0.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
major: int
|
|
57
|
+
minor: int
|
|
58
|
+
patch: int
|
|
59
|
+
prerelease: PreRelease
|
|
60
|
+
buildmetadata: BuildMetadata
|
|
61
|
+
|
|
62
|
+
@overload
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
version: Self,
|
|
66
|
+
/,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Initialize the instance."""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@overload
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
version_str: str,
|
|
75
|
+
/,
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Initialize the instance."""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
@overload
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
version_dict: VersionDictLike,
|
|
84
|
+
/,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Initialize the instance."""
|
|
87
|
+
...
|
|
88
|
+
|
|
89
|
+
@overload
|
|
90
|
+
def __init__(
|
|
91
|
+
self,
|
|
92
|
+
version_tuple: VersionTupleLike,
|
|
93
|
+
/,
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Initialize the instance."""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
@overload
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
major: int,
|
|
102
|
+
minor: int,
|
|
103
|
+
patch: int,
|
|
104
|
+
prerelease: PreRelease = None,
|
|
105
|
+
buildmetadata: BuildMetadata = None,
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Initialize the instance."""
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
111
|
+
"""Initialize the instance."""
|
|
112
|
+
has_1_pos_arg = len(args) == 1 and len(kwargs) == 0
|
|
113
|
+
# Version
|
|
114
|
+
if has_1_pos_arg and isinstance(args[0], Version):
|
|
115
|
+
version = args[0]
|
|
116
|
+
version_dict = version.to_dict(exclude_none=False)
|
|
117
|
+
|
|
118
|
+
# Version str
|
|
119
|
+
elif has_1_pos_arg and isinstance(args[0], str):
|
|
120
|
+
version_str = args[0]
|
|
121
|
+
version_dict = _parse_version_str(version_str)
|
|
122
|
+
|
|
123
|
+
# Version dict
|
|
124
|
+
elif has_1_pos_arg and isinstance_generic(args[0], VersionDictLike):
|
|
125
|
+
version_dict = args[0]
|
|
126
|
+
|
|
127
|
+
# Version tuple
|
|
128
|
+
elif has_1_pos_arg and isinstance_generic(args[0], VersionTupleLike):
|
|
129
|
+
version_tuple = args[0]
|
|
130
|
+
version_dict = dict(zip(_VERSION_KEYS, version_tuple))
|
|
131
|
+
|
|
132
|
+
# Version args/kwargs
|
|
133
|
+
else:
|
|
134
|
+
version_dict = dict(zip(_VERSION_KEYS, args))
|
|
135
|
+
intersection = tuple(set(version_dict.keys()).intersection(kwargs.keys()))
|
|
136
|
+
if len(intersection) > 0:
|
|
137
|
+
msg = f"Got multiple values for argument(s) {intersection}. (with {args=} and {kwargs=})"
|
|
138
|
+
raise TypeError(msg)
|
|
139
|
+
version_dict.update(kwargs) # type: ignore
|
|
140
|
+
|
|
141
|
+
invalid = tuple(set(version_dict.keys()).difference(_VERSION_KEYS))
|
|
142
|
+
if len(invalid) > 0:
|
|
143
|
+
msg = f"Got an unexpected arguments {invalid=}. (with {args=} and {kwargs=})"
|
|
144
|
+
raise TypeError(msg)
|
|
145
|
+
|
|
146
|
+
if not isinstance_generic(version_dict, VersionDict):
|
|
147
|
+
msg = f"Invalid argument {args=} and {kwargs=}. (invalid argument types, expected (major=int, minor=int, patch=int, prerelease={PreRelease}, buildmetadata={BuildMetadata}))"
|
|
148
|
+
raise ValueError(msg)
|
|
149
|
+
|
|
150
|
+
major = version_dict["major"]
|
|
151
|
+
minor = version_dict["minor"]
|
|
152
|
+
patch = version_dict["patch"]
|
|
153
|
+
prerelease = version_dict.get("prerelease", None)
|
|
154
|
+
buildmetadata = version_dict.get("buildmetadata", None)
|
|
155
|
+
|
|
156
|
+
self.major = major # type: ignore
|
|
157
|
+
self.minor = minor # type: ignore
|
|
158
|
+
self.patch = patch # type: ignore
|
|
159
|
+
self.prerelease = prerelease # type: ignore
|
|
160
|
+
self.buildmetadata = buildmetadata # type: ignore
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
def from_dict(cls, version_dict: VersionDictLike) -> Self:
|
|
164
|
+
"""Create an instance from dict."""
|
|
165
|
+
return cls(version_dict)
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def from_str(cls, version_str: str) -> Self:
|
|
169
|
+
"""Create an instance from str."""
|
|
170
|
+
return cls(version_str)
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
def from_tuple(cls, version_tuple: VersionTupleLike) -> Self:
|
|
174
|
+
"""Create an instance from tuple."""
|
|
175
|
+
return cls(version_tuple)
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def python(cls, releaselevel_in_metadata: bool = False) -> Self:
|
|
179
|
+
"""Create an instance of Version with Python version.
|
|
180
|
+
|
|
181
|
+
Note: Python 'micro' value is mapped to 'patch'.
|
|
182
|
+
"""
|
|
183
|
+
if releaselevel_in_metadata:
|
|
184
|
+
buildmetadata = sys.version_info.releaselevel
|
|
185
|
+
else:
|
|
186
|
+
buildmetadata = None
|
|
187
|
+
|
|
188
|
+
return cls(
|
|
189
|
+
major=sys.version_info.major,
|
|
190
|
+
minor=sys.version_info.minor,
|
|
191
|
+
patch=sys.version_info.micro,
|
|
192
|
+
buildmetadata=buildmetadata,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def micro(self) -> int:
|
|
197
|
+
"""Getter alias of 'patch'."""
|
|
198
|
+
return self.patch
|
|
199
|
+
|
|
200
|
+
@micro.setter
|
|
201
|
+
def micro(self, new_value: int) -> None:
|
|
202
|
+
"""Setter alias of 'patch'."""
|
|
203
|
+
self.patch = new_value
|
|
204
|
+
|
|
205
|
+
def without_prerelease(self) -> "Version":
|
|
206
|
+
"""Perform the without prerelease operation."""
|
|
207
|
+
return Version(self.major, self.minor, self.patch, None, self.buildmetadata)
|
|
208
|
+
|
|
209
|
+
def without_buildmetadata(self) -> "Version":
|
|
210
|
+
"""Perform the without buildmetadata operation."""
|
|
211
|
+
return Version(self.major, self.minor, self.patch, self.prerelease, None)
|
|
212
|
+
|
|
213
|
+
def next_major(
|
|
214
|
+
self,
|
|
215
|
+
keep_prerelease: bool = False,
|
|
216
|
+
keep_buildmetadata: bool = False,
|
|
217
|
+
) -> "Version":
|
|
218
|
+
"""Perform the next major operation."""
|
|
219
|
+
prerelease = self.prerelease if keep_prerelease else None
|
|
220
|
+
buildmetadata = self.buildmetadata if keep_buildmetadata else None
|
|
221
|
+
return Version(
|
|
222
|
+
major=self.major + 1,
|
|
223
|
+
minor=0,
|
|
224
|
+
patch=0,
|
|
225
|
+
prerelease=prerelease,
|
|
226
|
+
buildmetadata=buildmetadata,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def next_minor(
|
|
230
|
+
self,
|
|
231
|
+
keep_prerelease: bool = False,
|
|
232
|
+
keep_buildmetadata: bool = False,
|
|
233
|
+
) -> "Version":
|
|
234
|
+
"""Perform the next minor operation."""
|
|
235
|
+
prerelease = self.prerelease if keep_prerelease else None
|
|
236
|
+
buildmetadata = self.buildmetadata if keep_buildmetadata else None
|
|
237
|
+
return Version(
|
|
238
|
+
major=self.major,
|
|
239
|
+
minor=self.minor + 1,
|
|
240
|
+
patch=0,
|
|
241
|
+
prerelease=prerelease,
|
|
242
|
+
buildmetadata=buildmetadata,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def next_patch(
|
|
246
|
+
self,
|
|
247
|
+
keep_prerelease: bool = False,
|
|
248
|
+
keep_buildmetadata: bool = False,
|
|
249
|
+
) -> "Version":
|
|
250
|
+
"""Perform the next patch operation."""
|
|
251
|
+
prerelease = self.prerelease if keep_prerelease else None
|
|
252
|
+
buildmetadata = self.buildmetadata if keep_buildmetadata else None
|
|
253
|
+
return Version(
|
|
254
|
+
major=self.major,
|
|
255
|
+
minor=self.minor,
|
|
256
|
+
patch=self.patch + 1,
|
|
257
|
+
prerelease=prerelease,
|
|
258
|
+
buildmetadata=buildmetadata,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def to_dict(self, exclude_none: bool = True) -> VersionDict:
|
|
262
|
+
"""Convert the value to dict."""
|
|
263
|
+
version_dict = asdict(self)
|
|
264
|
+
if exclude_none:
|
|
265
|
+
version_dict = {k: v for k, v in version_dict.items() if v is not None}
|
|
266
|
+
return version_dict # type: ignore
|
|
267
|
+
|
|
268
|
+
def to_str(self) -> str:
|
|
269
|
+
"""Convert the value to str."""
|
|
270
|
+
kwds = dict(
|
|
271
|
+
major=self.major,
|
|
272
|
+
minor=self.minor,
|
|
273
|
+
patch=self.patch,
|
|
274
|
+
)
|
|
275
|
+
version_str = _VERSION_FORMAT.format(**kwds)
|
|
276
|
+
if self.prerelease is not None:
|
|
277
|
+
version_str = f"{version_str}-{self.prerelease}"
|
|
278
|
+
if self.buildmetadata is not None:
|
|
279
|
+
version_str = f"{version_str}+{self.buildmetadata}"
|
|
280
|
+
|
|
281
|
+
return version_str
|
|
282
|
+
|
|
283
|
+
def to_tuple(
|
|
284
|
+
self,
|
|
285
|
+
exclude_none: bool = True,
|
|
286
|
+
) -> VersionTuple:
|
|
287
|
+
"""Convert the value to tuple."""
|
|
288
|
+
version_tuple = tuple(self.to_dict(exclude_none).values())
|
|
289
|
+
return version_tuple # type: ignore
|
|
290
|
+
|
|
291
|
+
def equals(self, other: VersionLike, *, ignore_buildmetadata: bool = False) -> bool:
|
|
292
|
+
"""Perform the equals operation."""
|
|
293
|
+
if isinstance(other, (Mapping, tuple, str)):
|
|
294
|
+
other = Version(other)
|
|
295
|
+
# note: use self.__class__ to avoid error cause by 'pytest -v test' collect
|
|
296
|
+
elif not isinstance(other, (Version, self.__class__)):
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
self.major == other.major
|
|
301
|
+
and self.minor == other.minor
|
|
302
|
+
and self.patch == other.patch
|
|
303
|
+
and self.prerelease == other.prerelease
|
|
304
|
+
and (ignore_buildmetadata or self.buildmetadata == other.buildmetadata)
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def __str__(self) -> str:
|
|
308
|
+
"""Return the string representation of the instance."""
|
|
309
|
+
return self.to_str()
|
|
310
|
+
|
|
311
|
+
def __eq__(self, other: Any) -> bool:
|
|
312
|
+
"""Return whether this instance equals another object."""
|
|
313
|
+
return self.equals(other)
|
|
314
|
+
|
|
315
|
+
def __lt__(self, other: VersionLike) -> bool:
|
|
316
|
+
"""Return whether this instance is less than another."""
|
|
317
|
+
return _compare_lt(self, other)
|
|
318
|
+
|
|
319
|
+
def __le__(self, other: VersionLike) -> bool:
|
|
320
|
+
"""Return whether this instance is less than or equal to another."""
|
|
321
|
+
return (self == other) or (self < other)
|
|
322
|
+
|
|
323
|
+
def __gt__(self, other: VersionLike) -> bool:
|
|
324
|
+
"""Return whether this instance is greater than another."""
|
|
325
|
+
return _compare_lt(other, self)
|
|
326
|
+
|
|
327
|
+
def __ge__(self, other: VersionLike) -> bool:
|
|
328
|
+
"""Return whether this instance is greater than or equal to another."""
|
|
329
|
+
return (self == other) or (self > other)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _compare_lt(
|
|
333
|
+
x: Union[Version, Mapping, tuple, str], y: Union[Version, Mapping, tuple, str]
|
|
334
|
+
) -> bool:
|
|
335
|
+
"""Perform the compare lt operation."""
|
|
336
|
+
if isinstance(x, (Mapping, tuple, str)):
|
|
337
|
+
x = Version(x)
|
|
338
|
+
if isinstance(y, (Mapping, tuple, str)):
|
|
339
|
+
y = Version(y)
|
|
340
|
+
|
|
341
|
+
self_tuple = x.to_tuple(exclude_none=False)
|
|
342
|
+
other_tuple = y.to_tuple(exclude_none=False)
|
|
343
|
+
|
|
344
|
+
self_tuple = self_tuple[:4]
|
|
345
|
+
other_tuple = other_tuple[:4]
|
|
346
|
+
|
|
347
|
+
for self_v, other_v in zip(self_tuple, other_tuple):
|
|
348
|
+
if self_v == other_v:
|
|
349
|
+
continue
|
|
350
|
+
if self_v is None and other_v is not None:
|
|
351
|
+
return False
|
|
352
|
+
if self_v is not None and other_v is None:
|
|
353
|
+
return True
|
|
354
|
+
|
|
355
|
+
if isinstance(self_v, (int, str, NoneType)):
|
|
356
|
+
self_v = [self_v]
|
|
357
|
+
elif not isinstance(self_v, list):
|
|
358
|
+
raise TypeError(f"Invalid argument type {type(self_v)}.")
|
|
359
|
+
|
|
360
|
+
if isinstance(other_v, (int, str, NoneType)):
|
|
361
|
+
other_v = [other_v]
|
|
362
|
+
elif not isinstance(other_v, list):
|
|
363
|
+
raise TypeError(f"Invalid argument type {type(other_v)}.")
|
|
364
|
+
|
|
365
|
+
minlen = min(len(self_v), len(other_v))
|
|
366
|
+
if len(self_v) != len(other_v) and self_v[:minlen] == other_v[:minlen]:
|
|
367
|
+
return len(self_v) < len(other_v)
|
|
368
|
+
|
|
369
|
+
for self_vi, other_vi in zip(self_v, other_v):
|
|
370
|
+
if self_vi == other_vi:
|
|
371
|
+
continue
|
|
372
|
+
if isinstance(self_vi, int) and isinstance(other_vi, int):
|
|
373
|
+
return self_vi < other_vi
|
|
374
|
+
if isinstance(self_vi, int) and isinstance(other_vi, str):
|
|
375
|
+
return True
|
|
376
|
+
if isinstance(self_vi, str) and isinstance(other_vi, int):
|
|
377
|
+
return False
|
|
378
|
+
if isinstance(self_vi, str) and isinstance(other_vi, str):
|
|
379
|
+
return self_vi < other_vi
|
|
380
|
+
|
|
381
|
+
msg = f"Invalid attribute type {self_vi=} and {other_vi=}."
|
|
382
|
+
raise TypeError(msg)
|
|
383
|
+
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _parse_version_str(version_str: str) -> VersionDict:
|
|
388
|
+
"""Parse version str."""
|
|
389
|
+
version_match = re.match(_VERSION_PATTERN, version_str)
|
|
390
|
+
if version_match is None:
|
|
391
|
+
msg = f"Invalid argument {version_str=}. (not a version)"
|
|
392
|
+
raise ValueError(msg)
|
|
393
|
+
|
|
394
|
+
version_dict = version_match.groupdict()
|
|
395
|
+
result = {}
|
|
396
|
+
for k, v in version_dict.items():
|
|
397
|
+
if isinstance(v, str) and "." in v:
|
|
398
|
+
v = v.split(".")
|
|
399
|
+
else:
|
|
400
|
+
v = [v]
|
|
401
|
+
|
|
402
|
+
v = [int(vi) if isinstance(vi, str) and vi.isdigit() else vi for vi in v]
|
|
403
|
+
if len(v) == 1:
|
|
404
|
+
v = v[0]
|
|
405
|
+
result[k] = v
|
|
406
|
+
return result # type: ignore
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import lazy_loader as lazy # type: ignore
|
|
8
|
+
except ImportError:
|
|
9
|
+
lazy = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING or lazy is None:
|
|
13
|
+
from .csv import dump_csv, dumps_csv, load_csv, loads_csv, read_csv, save_csv
|
|
14
|
+
from .json import dump_json, dumps_json, load_json, loads_json, read_json, save_json
|
|
15
|
+
from .jsonl import (
|
|
16
|
+
dump_jsonl,
|
|
17
|
+
dumps_jsonl,
|
|
18
|
+
load_jsonl,
|
|
19
|
+
loads_jsonl,
|
|
20
|
+
read_jsonl,
|
|
21
|
+
save_jsonl,
|
|
22
|
+
)
|
|
23
|
+
from .pickle import (
|
|
24
|
+
dump_pickle,
|
|
25
|
+
dumps_pickle,
|
|
26
|
+
load_pickle,
|
|
27
|
+
loads_pickle,
|
|
28
|
+
read_pickle,
|
|
29
|
+
save_pickle,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
else:
|
|
33
|
+
__getattr__, __dir__, __all__ = lazy.attach(
|
|
34
|
+
__name__,
|
|
35
|
+
submodules=["csv", "json", "jsonl", "pickle"],
|
|
36
|
+
submod_attrs={
|
|
37
|
+
"csv": [
|
|
38
|
+
"dump_csv",
|
|
39
|
+
"dumps_csv",
|
|
40
|
+
"load_csv",
|
|
41
|
+
"loads_csv",
|
|
42
|
+
"read_csv",
|
|
43
|
+
"save_csv",
|
|
44
|
+
],
|
|
45
|
+
"json": [
|
|
46
|
+
"dump_json",
|
|
47
|
+
"dumps_json",
|
|
48
|
+
"load_json",
|
|
49
|
+
"loads_json",
|
|
50
|
+
"read_json",
|
|
51
|
+
"save_json",
|
|
52
|
+
],
|
|
53
|
+
"jsonl": [
|
|
54
|
+
"dump_jsonl",
|
|
55
|
+
"dumps_jsonl",
|
|
56
|
+
"load_jsonl",
|
|
57
|
+
"loads_jsonl",
|
|
58
|
+
"read_jsonl",
|
|
59
|
+
"save_jsonl",
|
|
60
|
+
],
|
|
61
|
+
"pickle": [
|
|
62
|
+
"dump_pickle",
|
|
63
|
+
"dumps_pickle",
|
|
64
|
+
"load_pickle",
|
|
65
|
+
"loads_pickle",
|
|
66
|
+
"read_pickle",
|
|
67
|
+
"save_pickle",
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from io import TextIOWrapper
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import (
|
|
8
|
+
Union,
|
|
9
|
+
overload,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@overload
|
|
14
|
+
def _setup_output_fpath(
|
|
15
|
+
fpath: Union[str, Path, os.PathLike],
|
|
16
|
+
*,
|
|
17
|
+
overwrite: bool = True,
|
|
18
|
+
make_parents: bool = True,
|
|
19
|
+
absolute: bool = True,
|
|
20
|
+
) -> Path:
|
|
21
|
+
"""Perform the setup output fpath operation."""
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@overload
|
|
26
|
+
def _setup_output_fpath(
|
|
27
|
+
fpath: TextIOWrapper,
|
|
28
|
+
*,
|
|
29
|
+
overwrite: bool = True,
|
|
30
|
+
make_parents: bool = True,
|
|
31
|
+
absolute: bool = True,
|
|
32
|
+
) -> TextIOWrapper:
|
|
33
|
+
"""Perform the setup output fpath operation."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@overload
|
|
38
|
+
def _setup_output_fpath(
|
|
39
|
+
fpath: None,
|
|
40
|
+
*,
|
|
41
|
+
overwrite: bool = True,
|
|
42
|
+
make_parents: bool = True,
|
|
43
|
+
absolute: bool = True,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Perform the setup output fpath operation."""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _setup_output_fpath(
|
|
50
|
+
fpath: Union[str, Path, os.PathLike, TextIOWrapper, None],
|
|
51
|
+
*,
|
|
52
|
+
overwrite: bool = True,
|
|
53
|
+
make_parents: bool = True,
|
|
54
|
+
absolute: bool = True,
|
|
55
|
+
) -> Union[Path, None, TextIOWrapper]:
|
|
56
|
+
"""Resolve path, expand path and create intermediate parents."""
|
|
57
|
+
if not isinstance(fpath, (str, Path, os.PathLike)):
|
|
58
|
+
return fpath
|
|
59
|
+
|
|
60
|
+
fpath = Path(fpath)
|
|
61
|
+
if absolute:
|
|
62
|
+
fpath = fpath.resolve().expanduser()
|
|
63
|
+
|
|
64
|
+
if not overwrite and fpath.exists():
|
|
65
|
+
msg = f"File {fpath} already exists."
|
|
66
|
+
raise FileExistsError(msg)
|
|
67
|
+
elif make_parents:
|
|
68
|
+
fpath.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
|
|
70
|
+
return fpath
|