pythonwrench 0.6.5__tar.gz → 0.6.7__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.
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/CHANGELOG.md +14 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/CITATION.cff +2 -2
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/PKG-INFO +1 -1
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/__init__.py +1 -1
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/argparse/dataclass_.py +16 -2
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/logging.py +5 -4
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/serialization/_core.py +3 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/serialization/csv.py +6 -2
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/serialization/json.py +47 -9
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/serialization/jsonl.py +34 -9
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/serialization/pickle.py +50 -10
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/warnings.py +2 -2
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_argparse.py +10 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_typing.py +15 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/.github/workflows/test.yaml +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/.gitignore +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/.pre-commit-config.yaml +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/.readthedocs.yaml +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/LICENSE +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/README.md +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/Makefile +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/conf.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/index.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/make.bat +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/modules.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.abc.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.argparse.dataclass_.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.argparse.parsers.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.argparse.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.cast.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.checksum.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.collections.collections.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.collections.prop.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.collections.reducers.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.collections.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.concurrent.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.csv.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.dataclasses.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.datetime.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.difflib.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.disk_cache.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.enum.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.functools.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.hashlib.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.importlib.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.inspect.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.json.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.jsonl.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.logging.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.math.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.os.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.pickle.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.random.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.re.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.semver.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.csv.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.json.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.jsonl.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.pickle.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.time.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.typing.checks.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.typing.classes.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.typing.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/pythonwrench.warnings.rst +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/docs/update.bash +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/pyproject.toml +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/setup.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/_core.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/argparse/__init__.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/argparse/parsers.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/cast.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/checksum.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/collections/__init__.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/collections/collections.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/collections/reducers.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/concurrent.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/dataclasses.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/disk_cache.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/entrypoints/info.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/entrypoints/safe_rmdir.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/entrypoints/tree.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/functools.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/jsonl.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/os.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/pickle.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/re.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/semver.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/serialization/__init__.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/time.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/typing/__init__.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/typing/checks.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/src/pythonwrench/typing/classes.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_abc.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_cast.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_checksum.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_collections.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_core.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_csv.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_difflib.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_disk_cache.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_entrypoints.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_enum.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_functools.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_importlib.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_inspect.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_json.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_logging.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_math.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_os.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_random.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_readme.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/tests/test_semver.py +0 -0
- {pythonwrench-0.6.5 → pythonwrench-0.6.7}/uv.lock +0 -0
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.7] 2026-09-07
|
|
6
|
+
### Added
|
|
7
|
+
- `on_error` and `default` arguments to `load_json`, `load_pickle`, `load_jsonl` functions.
|
|
8
|
+
- `posonly_for_required` argument for `parse_args_using_dataclass`.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `setup_logging_level` when stderr is not set in root logger.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.6.6] 2026-08-20
|
|
15
|
+
### Fixed
|
|
16
|
+
- `setup_logging_level` isinstance check.
|
|
17
|
+
|
|
18
|
+
|
|
5
19
|
## [0.6.5] 2026-08-20
|
|
6
20
|
### Added
|
|
7
21
|
- `return_single_namespace` option for `parse_args_using_dataclass` function.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.7
|
|
4
4
|
Summary: Python library with tools for typing, manipulating collections, and more!
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/pythonwrench/
|
|
6
6
|
Project-URL: Documentation, https://pythonwrench.readthedocs.io/
|
|
@@ -58,6 +58,7 @@ def parse_args_using_dataclass(
|
|
|
58
58
|
bool_action: BoolAction = "store",
|
|
59
59
|
add_dashed_arg: bool = True,
|
|
60
60
|
return_single_namespace: Literal[True],
|
|
61
|
+
posonly_for_required: bool = False,
|
|
61
62
|
) -> Namespace: ...
|
|
62
63
|
|
|
63
64
|
|
|
@@ -71,6 +72,7 @@ def parse_args_using_dataclass(
|
|
|
71
72
|
bool_action: BoolAction = "store",
|
|
72
73
|
add_dashed_arg: bool = True,
|
|
73
74
|
return_single_namespace: Literal[False] = False,
|
|
75
|
+
posonly_for_required: bool = False,
|
|
74
76
|
) -> T_DataclassInstance: ...
|
|
75
77
|
|
|
76
78
|
|
|
@@ -86,6 +88,7 @@ def parse_args_using_dataclass(
|
|
|
86
88
|
bool_action: BoolAction = "store",
|
|
87
89
|
add_dashed_arg: bool = True,
|
|
88
90
|
return_single_namespace: Literal[False] = False,
|
|
91
|
+
posonly_for_required: bool = False,
|
|
89
92
|
) -> Tuple[
|
|
90
93
|
T_DataclassInstance,
|
|
91
94
|
T_DataclassInstance_2,
|
|
@@ -105,6 +108,7 @@ def parse_args_using_dataclass(
|
|
|
105
108
|
bool_action: BoolAction = "store",
|
|
106
109
|
add_dashed_arg: bool = True,
|
|
107
110
|
return_single_namespace: Literal[False] = False,
|
|
111
|
+
posonly_for_required: bool = False,
|
|
108
112
|
) -> Tuple[
|
|
109
113
|
T_DataclassInstance,
|
|
110
114
|
T_DataclassInstance_2,
|
|
@@ -126,6 +130,7 @@ def parse_args_using_dataclass(
|
|
|
126
130
|
bool_action: BoolAction = "store",
|
|
127
131
|
add_dashed_arg: bool = True,
|
|
128
132
|
return_single_namespace: Literal[False] = False,
|
|
133
|
+
posonly_for_required: bool = False,
|
|
129
134
|
) -> Tuple[
|
|
130
135
|
T_DataclassInstance,
|
|
131
136
|
T_DataclassInstance_2,
|
|
@@ -149,6 +154,7 @@ def parse_args_using_dataclass(
|
|
|
149
154
|
bool_action: BoolAction = "store",
|
|
150
155
|
add_dashed_arg: bool = True,
|
|
151
156
|
return_single_namespace: Literal[False] = False,
|
|
157
|
+
posonly_for_required: bool = False,
|
|
152
158
|
) -> Tuple[
|
|
153
159
|
T_DataclassInstance,
|
|
154
160
|
T_DataclassInstance_2,
|
|
@@ -167,6 +173,7 @@ def parse_args_using_dataclass(
|
|
|
167
173
|
bool_action: BoolAction = "store",
|
|
168
174
|
add_dashed_arg: bool = True,
|
|
169
175
|
return_single_namespace: bool = False,
|
|
176
|
+
posonly_for_required: bool = False,
|
|
170
177
|
) -> Union[
|
|
171
178
|
DataclassInstance,
|
|
172
179
|
Tuple[DataclassInstance, ...],
|
|
@@ -187,6 +194,7 @@ def parse_args_using_dataclass(
|
|
|
187
194
|
list_parsing=list_parsing,
|
|
188
195
|
bool_action=bool_action,
|
|
189
196
|
add_dashed_arg=add_dashed_arg,
|
|
197
|
+
posonly_for_required=posonly_for_required,
|
|
190
198
|
)
|
|
191
199
|
assert parser is not None
|
|
192
200
|
|
|
@@ -223,17 +231,23 @@ def add_dataclass_fields_to_parser(
|
|
|
223
231
|
list_parsing: ListParsing = "argparse",
|
|
224
232
|
bool_action: BoolAction = "store",
|
|
225
233
|
add_dashed_arg: bool = True,
|
|
234
|
+
posonly_for_required: bool = False,
|
|
226
235
|
) -> ArgumentParser:
|
|
227
236
|
"""Perform the add dataclass fields to parser operation."""
|
|
237
|
+
if posonly_for_required:
|
|
238
|
+
prefix = ""
|
|
239
|
+
else:
|
|
240
|
+
prefix = "--"
|
|
241
|
+
|
|
228
242
|
if parser is None:
|
|
229
243
|
parser = ArgumentParser()
|
|
230
244
|
|
|
231
245
|
for field in fields(dataclass_type):
|
|
232
246
|
kwds = {}
|
|
233
|
-
posargs = [f"
|
|
247
|
+
posargs = [f"{prefix}{field.name}"]
|
|
234
248
|
if add_dashed_arg and "_" in field.name:
|
|
235
249
|
dashed_arg_name = field.name.replace("_", "-")
|
|
236
|
-
posargs.append(f"
|
|
250
|
+
posargs.append(f"{prefix}{dashed_arg_name}")
|
|
237
251
|
|
|
238
252
|
if field.default is MISSING and field.default_factory is MISSING:
|
|
239
253
|
if bool_action != "store" and field.type is bool:
|
|
@@ -5,10 +5,11 @@ import inspect
|
|
|
5
5
|
import logging
|
|
6
6
|
import sys
|
|
7
7
|
from functools import lru_cache
|
|
8
|
+
from io import TextIOBase
|
|
8
9
|
from logging import FileHandler, Formatter, Logger, StreamHandler
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from types import ModuleType
|
|
11
|
-
from typing import IO, Iterable, List, Literal, Optional, TypeVar, Union
|
|
12
|
+
from typing import IO, Iterable, List, Literal, Optional, TextIO, TypeVar, Union
|
|
12
13
|
|
|
13
14
|
from typing_extensions import TypeAlias
|
|
14
15
|
|
|
@@ -117,11 +118,11 @@ def setup_logging_level(
|
|
|
117
118
|
for handler in logging.getLogger().handlers
|
|
118
119
|
if isinstance(handler, StreamHandler)
|
|
119
120
|
]
|
|
120
|
-
elif isinstance(stream,
|
|
121
|
+
elif isinstance(stream, (TextIOBase, TextIO)):
|
|
121
122
|
streams = [stream]
|
|
122
123
|
elif stream is None:
|
|
123
124
|
streams = stream
|
|
124
|
-
elif isinstance_generic(stream, Iterable[
|
|
125
|
+
elif isinstance_generic(stream, Iterable[Union[TextIOBase, TextIO]]):
|
|
125
126
|
streams = list(stream)
|
|
126
127
|
else:
|
|
127
128
|
raise TypeError(f"Invalid argument type {type(stream)=}.")
|
|
@@ -147,7 +148,7 @@ def setup_logging_level(
|
|
|
147
148
|
|
|
148
149
|
try:
|
|
149
150
|
index = streams.index(handler.stream)
|
|
150
|
-
except
|
|
151
|
+
except ValueError:
|
|
151
152
|
continue
|
|
152
153
|
|
|
153
154
|
handler.setFormatter(fmt)
|
|
@@ -73,7 +73,9 @@ def dump_csv(
|
|
|
73
73
|
**csv_writer_kwds,
|
|
74
74
|
)
|
|
75
75
|
|
|
76
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
76
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
77
|
+
return content
|
|
78
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
77
79
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
78
80
|
with open(file, "w") as opened_file:
|
|
79
81
|
opened_file.write(content)
|
|
@@ -152,7 +154,9 @@ def save_csv(
|
|
|
152
154
|
replace_newline_by: Replace newline character to avoid newline in CSV content. defaults to "\\n".
|
|
153
155
|
\*\*csv_writer_kwds: Others optional arguments passed to CSV writer object.
|
|
154
156
|
"""
|
|
155
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
157
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
158
|
+
return None
|
|
159
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
156
160
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
157
161
|
file = open(file, "w")
|
|
158
162
|
close = True
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
from io import StringIO, TextIOBase
|
|
6
|
+
from json import JSONDecodeError
|
|
6
7
|
from os import PathLike
|
|
7
8
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Optional, Union
|
|
9
|
+
from typing import Any, Optional, Union, get_args
|
|
9
10
|
|
|
10
11
|
from pythonwrench.cast import as_builtin
|
|
11
12
|
from pythonwrench.functools import function_alias
|
|
12
|
-
from pythonwrench.serialization._core import _setup_output_fpath
|
|
13
|
+
from pythonwrench.serialization._core import OnError, _setup_output_fpath
|
|
13
14
|
|
|
14
15
|
# -- Dump / Save / Serialize content to JSON --
|
|
15
16
|
|
|
@@ -50,7 +51,9 @@ def dump_json(
|
|
|
50
51
|
**json_dumps_kwds,
|
|
51
52
|
)
|
|
52
53
|
|
|
53
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
54
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
55
|
+
return content
|
|
56
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
54
57
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
55
58
|
with open(file, "w") as opened_file:
|
|
56
59
|
opened_file.write(content)
|
|
@@ -146,6 +149,9 @@ def _serialize_json(
|
|
|
146
149
|
def load_json(
|
|
147
150
|
file: Union[str, Path, PathLike, TextIOBase],
|
|
148
151
|
/,
|
|
152
|
+
*,
|
|
153
|
+
on_error: OnError = "raise",
|
|
154
|
+
default: Any = None,
|
|
149
155
|
**json_loads_kwds,
|
|
150
156
|
) -> Any:
|
|
151
157
|
"""Load json."""
|
|
@@ -155,16 +161,33 @@ def load_json(
|
|
|
155
161
|
else:
|
|
156
162
|
close = False
|
|
157
163
|
|
|
158
|
-
data = _parse_json(
|
|
164
|
+
data = _parse_json(
|
|
165
|
+
file,
|
|
166
|
+
on_error=on_error,
|
|
167
|
+
default=default,
|
|
168
|
+
**json_loads_kwds,
|
|
169
|
+
)
|
|
159
170
|
if close:
|
|
160
171
|
file.close()
|
|
161
172
|
return data
|
|
162
173
|
|
|
163
174
|
|
|
164
|
-
def loads_json(
|
|
165
|
-
|
|
175
|
+
def loads_json(
|
|
176
|
+
content: str,
|
|
177
|
+
/,
|
|
178
|
+
*,
|
|
179
|
+
on_error: OnError = "raise",
|
|
180
|
+
default: Any = None,
|
|
181
|
+
**json_loads_kwds,
|
|
182
|
+
) -> Any:
|
|
183
|
+
"""Parse JSON string to object."""
|
|
166
184
|
with StringIO(content) as buffer:
|
|
167
|
-
return _parse_json(
|
|
185
|
+
return _parse_json(
|
|
186
|
+
buffer,
|
|
187
|
+
on_error=on_error,
|
|
188
|
+
default=default,
|
|
189
|
+
**json_loads_kwds,
|
|
190
|
+
)
|
|
168
191
|
|
|
169
192
|
|
|
170
193
|
@function_alias(load_json)
|
|
@@ -173,6 +196,21 @@ def read_json(*args, **kwargs):
|
|
|
173
196
|
...
|
|
174
197
|
|
|
175
198
|
|
|
176
|
-
def _parse_json(
|
|
199
|
+
def _parse_json(
|
|
200
|
+
buffer: TextIOBase,
|
|
201
|
+
*,
|
|
202
|
+
on_error: OnError,
|
|
203
|
+
default: Any = None,
|
|
204
|
+
**json_loads_kwds,
|
|
205
|
+
) -> Any:
|
|
177
206
|
"""Parse json."""
|
|
178
|
-
|
|
207
|
+
try:
|
|
208
|
+
return json.load(buffer, **json_loads_kwds)
|
|
209
|
+
except JSONDecodeError as err:
|
|
210
|
+
if on_error == "raise":
|
|
211
|
+
raise err
|
|
212
|
+
elif on_error == "default":
|
|
213
|
+
return default
|
|
214
|
+
else:
|
|
215
|
+
msg = f"Invalid argument {on_error=}. (expected one of {get_args(OnError)})"
|
|
216
|
+
raise ValueError(msg)
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
from io import StringIO, TextIOBase
|
|
5
5
|
from os import PathLike
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Union
|
|
7
|
+
from typing import Any, Union
|
|
8
8
|
|
|
9
9
|
from pythonwrench.cast import as_builtin
|
|
10
10
|
from pythonwrench.functools import function_alias
|
|
11
11
|
from pythonwrench.semver import Version
|
|
12
|
-
from pythonwrench.serialization._core import _setup_output_fpath
|
|
12
|
+
from pythonwrench.serialization._core import OnError, _setup_output_fpath
|
|
13
13
|
from pythonwrench.serialization.json import (
|
|
14
14
|
_serialize_json,
|
|
15
15
|
dumps_json,
|
|
@@ -63,7 +63,9 @@ def dump_jsonl(
|
|
|
63
63
|
**json_dumps_kwds,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
66
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
67
|
+
return content
|
|
68
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
67
69
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
68
70
|
with open(file, "w") as opened_file:
|
|
69
71
|
opened_file.write(content)
|
|
@@ -113,7 +115,9 @@ def save_jsonl(
|
|
|
113
115
|
**json_dumps_kwds,
|
|
114
116
|
) -> None:
|
|
115
117
|
"""Save jsonl."""
|
|
116
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
118
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
119
|
+
return None
|
|
120
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
117
121
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
118
122
|
file = open(file, "w")
|
|
119
123
|
close = True
|
|
@@ -163,6 +167,9 @@ def _serialize_jsonl(
|
|
|
163
167
|
def load_jsonl(
|
|
164
168
|
file: Union[str, Path, PathLike, TextIOBase],
|
|
165
169
|
/,
|
|
170
|
+
*,
|
|
171
|
+
on_error: OnError = "raise",
|
|
172
|
+
default: Any = None,
|
|
166
173
|
**json_loads_kwds,
|
|
167
174
|
) -> list:
|
|
168
175
|
"""Load jsonl."""
|
|
@@ -172,16 +179,25 @@ def load_jsonl(
|
|
|
172
179
|
else:
|
|
173
180
|
close = False
|
|
174
181
|
|
|
175
|
-
data = _parse_jsonl(file, **json_loads_kwds)
|
|
182
|
+
data = _parse_jsonl(file, on_error=on_error, default=default, **json_loads_kwds)
|
|
176
183
|
if close:
|
|
177
184
|
file.close()
|
|
178
185
|
return data
|
|
179
186
|
|
|
180
187
|
|
|
181
|
-
def loads_jsonl(
|
|
188
|
+
def loads_jsonl(
|
|
189
|
+
content: str,
|
|
190
|
+
/,
|
|
191
|
+
*,
|
|
192
|
+
on_error: OnError = "raise",
|
|
193
|
+
default: Any = None,
|
|
194
|
+
**json_loads_kwds,
|
|
195
|
+
) -> list:
|
|
182
196
|
"""Load s jsonl."""
|
|
183
197
|
with StringIO(content) as buffer:
|
|
184
|
-
return _parse_jsonl(
|
|
198
|
+
return _parse_jsonl(
|
|
199
|
+
buffer, on_error=on_error, default=default, **json_loads_kwds
|
|
200
|
+
)
|
|
185
201
|
|
|
186
202
|
|
|
187
203
|
@function_alias(load_json)
|
|
@@ -190,7 +206,14 @@ def read_jsonl(*args, **kwargs):
|
|
|
190
206
|
...
|
|
191
207
|
|
|
192
208
|
|
|
193
|
-
def _parse_jsonl(
|
|
209
|
+
def _parse_jsonl(
|
|
210
|
+
buffer: TextIOBase,
|
|
211
|
+
/,
|
|
212
|
+
*,
|
|
213
|
+
on_error: OnError = "raise",
|
|
214
|
+
default: Any = None,
|
|
215
|
+
**json_loads_kwds,
|
|
216
|
+
) -> list:
|
|
194
217
|
"""Parse jsonl."""
|
|
195
218
|
data_lst = []
|
|
196
219
|
while True:
|
|
@@ -198,7 +221,9 @@ def _parse_jsonl(buffer: TextIOBase, **json_loads_kwds) -> list:
|
|
|
198
221
|
if content == "":
|
|
199
222
|
break
|
|
200
223
|
content = _removesuffix(content, "\n")
|
|
201
|
-
data = loads_json(
|
|
224
|
+
data = loads_json(
|
|
225
|
+
content, on_error=on_error, default=default, **json_loads_kwds
|
|
226
|
+
)
|
|
202
227
|
data_lst.append(data)
|
|
203
228
|
return data_lst
|
|
204
229
|
|
|
@@ -6,11 +6,12 @@ import pickle
|
|
|
6
6
|
from io import BytesIO
|
|
7
7
|
from os import PathLike
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from
|
|
9
|
+
from pickle import UnpicklingError
|
|
10
|
+
from typing import Any, BinaryIO, Union, get_args
|
|
10
11
|
|
|
11
12
|
from pythonwrench.cast import as_builtin
|
|
12
13
|
from pythonwrench.functools import function_alias
|
|
13
|
-
from pythonwrench.serialization._core import _setup_output_fpath
|
|
14
|
+
from pythonwrench.serialization._core import OnError, _setup_output_fpath
|
|
14
15
|
|
|
15
16
|
# -- Dump / Save / Serialize content to PICKLE --
|
|
16
17
|
|
|
@@ -44,7 +45,9 @@ def dump_pickle(
|
|
|
44
45
|
**pkl_dumps_kwds,
|
|
45
46
|
)
|
|
46
47
|
|
|
47
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
48
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
49
|
+
return content
|
|
50
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
48
51
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
49
52
|
with open(file, "wb") as opened_file:
|
|
50
53
|
opened_file.write(content)
|
|
@@ -107,7 +110,9 @@ def save_pickle(
|
|
|
107
110
|
to_builtins: If True, converts data to builtin equivalent before saving. defaults to False.
|
|
108
111
|
\*\*pkl_dumps_kwds: Other args passed to `pickle.dumps`.
|
|
109
112
|
"""
|
|
110
|
-
if isinstance(file, (str, Path, PathLike)):
|
|
113
|
+
if not overwrite and isinstance(file, (str, Path, PathLike)):
|
|
114
|
+
return None
|
|
115
|
+
elif isinstance(file, (str, Path, PathLike)):
|
|
111
116
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
112
117
|
file = open(file, "wb")
|
|
113
118
|
close = True
|
|
@@ -145,7 +150,14 @@ def _serialize_pickle(
|
|
|
145
150
|
# -- Load / Read / Parse PICKLE content --
|
|
146
151
|
|
|
147
152
|
|
|
148
|
-
def load_pickle(
|
|
153
|
+
def load_pickle(
|
|
154
|
+
file: Union[str, Path, BinaryIO],
|
|
155
|
+
/,
|
|
156
|
+
*,
|
|
157
|
+
on_error: OnError = "raise",
|
|
158
|
+
default: Any = None,
|
|
159
|
+
**pkl_loads_kwds,
|
|
160
|
+
) -> Any:
|
|
149
161
|
r"""Load content from PICKLE file.
|
|
150
162
|
|
|
151
163
|
Args:
|
|
@@ -158,13 +170,20 @@ def load_pickle(file: Union[str, Path, BinaryIO], /, **pkl_loads_kwds) -> Any:
|
|
|
158
170
|
else:
|
|
159
171
|
close = False
|
|
160
172
|
|
|
161
|
-
data = _parse_pickle(file, **pkl_loads_kwds)
|
|
173
|
+
data = _parse_pickle(file, on_error=on_error, default=default, **pkl_loads_kwds)
|
|
162
174
|
if close:
|
|
163
175
|
file.close()
|
|
164
176
|
return data
|
|
165
177
|
|
|
166
178
|
|
|
167
|
-
def loads_pickle(
|
|
179
|
+
def loads_pickle(
|
|
180
|
+
content: bytes,
|
|
181
|
+
/,
|
|
182
|
+
*,
|
|
183
|
+
on_error: OnError = "raise",
|
|
184
|
+
default: Any = None,
|
|
185
|
+
**pkl_loads_kwds,
|
|
186
|
+
) -> Any:
|
|
168
187
|
r"""Load content from raw bytes.
|
|
169
188
|
|
|
170
189
|
Args:
|
|
@@ -172,7 +191,12 @@ def loads_pickle(content: bytes, /, **pkl_loads_kwds) -> Any:
|
|
|
172
191
|
\*\*pkl_loads_kwds: Other args passed to `pickle.loads`.
|
|
173
192
|
"""
|
|
174
193
|
with BytesIO(content) as buffer:
|
|
175
|
-
return _parse_pickle(
|
|
194
|
+
return _parse_pickle(
|
|
195
|
+
buffer,
|
|
196
|
+
on_error=on_error,
|
|
197
|
+
default=default,
|
|
198
|
+
**pkl_loads_kwds,
|
|
199
|
+
)
|
|
176
200
|
|
|
177
201
|
|
|
178
202
|
@function_alias(load_pickle)
|
|
@@ -181,6 +205,22 @@ def read_pickle(*args, **kwargs):
|
|
|
181
205
|
...
|
|
182
206
|
|
|
183
207
|
|
|
184
|
-
def _parse_pickle(
|
|
208
|
+
def _parse_pickle(
|
|
209
|
+
buffer: BinaryIO,
|
|
210
|
+
/,
|
|
211
|
+
*,
|
|
212
|
+
on_error: OnError = "raise",
|
|
213
|
+
default: Any = None,
|
|
214
|
+
**pkl_loads_kwds,
|
|
215
|
+
) -> Any:
|
|
185
216
|
"""Parse pickle."""
|
|
186
|
-
|
|
217
|
+
try:
|
|
218
|
+
return pickle.load(buffer, **pkl_loads_kwds)
|
|
219
|
+
except UnpicklingError as err:
|
|
220
|
+
if on_error == "raise":
|
|
221
|
+
raise err
|
|
222
|
+
elif on_error == "default":
|
|
223
|
+
return default
|
|
224
|
+
else:
|
|
225
|
+
msg = f"Invalid argument {on_error=}. (expected one of {get_args(OnError)})"
|
|
226
|
+
raise ValueError(msg)
|
|
@@ -20,7 +20,7 @@ def warn_once(
|
|
|
20
20
|
stacklevel: int = 1,
|
|
21
21
|
source: Any = None,
|
|
22
22
|
) -> None:
|
|
23
|
-
"""
|
|
23
|
+
"""Warn message once using warnings module."""
|
|
24
24
|
...
|
|
25
25
|
|
|
26
26
|
|
|
@@ -31,7 +31,7 @@ def warn_once(
|
|
|
31
31
|
stacklevel: int = 1,
|
|
32
32
|
source: Any = None,
|
|
33
33
|
) -> None:
|
|
34
|
-
"""
|
|
34
|
+
"""Warn message once using warnings module."""
|
|
35
35
|
...
|
|
36
36
|
|
|
37
37
|
|
|
@@ -331,6 +331,16 @@ class TestDataclassParser(TestCase):
|
|
|
331
331
|
)
|
|
332
332
|
assert cfg == target
|
|
333
333
|
|
|
334
|
+
def test_parse_list(self) -> None:
|
|
335
|
+
@dataclass
|
|
336
|
+
class Cfg:
|
|
337
|
+
constraints: Iterable[Optional[str]]
|
|
338
|
+
|
|
339
|
+
cfg = parse_args_using_dataclass(
|
|
340
|
+
Cfg, args=["--constraints", "[none]"], list_parsing="brackets"
|
|
341
|
+
)
|
|
342
|
+
assert cfg == Cfg([None])
|
|
343
|
+
|
|
334
344
|
|
|
335
345
|
if __name__ == "__main__":
|
|
336
346
|
unittest.main()
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
+
import logging
|
|
4
5
|
import unittest
|
|
5
6
|
from dataclasses import dataclass
|
|
7
|
+
from io import TextIOBase
|
|
8
|
+
from logging import StreamHandler
|
|
6
9
|
from numbers import Number
|
|
7
10
|
from pathlib import Path
|
|
8
11
|
from typing import (
|
|
@@ -331,6 +334,18 @@ class TestIsInstanceGuard(TestCase):
|
|
|
331
334
|
assert isinstance_generic([], List[int], check_only_first=True)
|
|
332
335
|
assert isinstance_generic([1, 2], List, check_only_first=True)
|
|
333
336
|
|
|
337
|
+
def test_io_check(self) -> None:
|
|
338
|
+
assert isinstance_generic([], Iterable[TextIOBase])
|
|
339
|
+
assert not isinstance_generic([1], Iterable[TextIOBase])
|
|
340
|
+
|
|
341
|
+
logger = logging.getLogger()
|
|
342
|
+
streams = [
|
|
343
|
+
handler.stream
|
|
344
|
+
for handler in logger.handlers
|
|
345
|
+
if isinstance(handler, StreamHandler)
|
|
346
|
+
]
|
|
347
|
+
assert isinstance_generic(streams, Iterable[TextIOBase])
|
|
348
|
+
|
|
334
349
|
|
|
335
350
|
class TestCheckArgsType(TestCase):
|
|
336
351
|
def test_example_1(self) -> None:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|