pythonwrench 0.6.6__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.
Files changed (131) hide show
  1. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/CHANGELOG.md +10 -0
  2. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/CITATION.cff +2 -2
  3. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/PKG-INFO +1 -1
  4. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/__init__.py +1 -1
  5. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/argparse/dataclass_.py +16 -2
  6. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/logging.py +1 -1
  7. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/serialization/_core.py +3 -0
  8. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/serialization/csv.py +6 -2
  9. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/serialization/json.py +47 -9
  10. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/serialization/jsonl.py +34 -9
  11. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/serialization/pickle.py +50 -10
  12. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/warnings.py +2 -2
  13. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_argparse.py +10 -0
  14. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/.github/workflows/test.yaml +0 -0
  15. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/.gitignore +0 -0
  16. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/.pre-commit-config.yaml +0 -0
  17. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/.readthedocs.yaml +0 -0
  18. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/LICENSE +0 -0
  19. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/README.md +0 -0
  20. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/Makefile +0 -0
  21. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/conf.py +0 -0
  22. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/index.rst +0 -0
  23. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/make.bat +0 -0
  24. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/modules.rst +0 -0
  25. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.abc.rst +0 -0
  26. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.argparse.dataclass_.rst +0 -0
  27. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.argparse.parsers.rst +0 -0
  28. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.argparse.rst +0 -0
  29. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.cast.rst +0 -0
  30. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.checksum.rst +0 -0
  31. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.collections.collections.rst +0 -0
  32. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.collections.prop.rst +0 -0
  33. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.collections.reducers.rst +0 -0
  34. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.collections.rst +0 -0
  35. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.concurrent.rst +0 -0
  36. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.csv.rst +0 -0
  37. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.dataclasses.rst +0 -0
  38. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.datetime.rst +0 -0
  39. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.difflib.rst +0 -0
  40. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.disk_cache.rst +0 -0
  41. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.enum.rst +0 -0
  42. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.functools.rst +0 -0
  43. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.hashlib.rst +0 -0
  44. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.importlib.rst +0 -0
  45. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.inspect.rst +0 -0
  46. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.json.rst +0 -0
  47. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.jsonl.rst +0 -0
  48. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.logging.rst +0 -0
  49. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.math.rst +0 -0
  50. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.os.rst +0 -0
  51. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.pickle.rst +0 -0
  52. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.random.rst +0 -0
  53. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.re.rst +0 -0
  54. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.rst +0 -0
  55. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.semver.rst +0 -0
  56. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.csv.rst +0 -0
  57. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.json.rst +0 -0
  58. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.jsonl.rst +0 -0
  59. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.pickle.rst +0 -0
  60. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.serialization.rst +0 -0
  61. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.time.rst +0 -0
  62. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.typing.checks.rst +0 -0
  63. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.typing.classes.rst +0 -0
  64. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.typing.rst +0 -0
  65. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/pythonwrench.warnings.rst +0 -0
  66. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/docs/update.bash +0 -0
  67. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/pyproject.toml +0 -0
  68. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/setup.py +0 -0
  69. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/__main__.py +0 -0
  70. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/_core.py +0 -0
  71. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/abc.py +0 -0
  72. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/argparse/__init__.py +0 -0
  73. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/argparse/parsers.py +0 -0
  74. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/cast.py +0 -0
  75. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/checksum.py +0 -0
  76. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/collections/__init__.py +0 -0
  77. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/collections/collections.py +0 -0
  78. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/collections/prop.py +0 -0
  79. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/collections/reducers.py +0 -0
  80. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/concurrent.py +0 -0
  81. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/csv.py +0 -0
  82. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/dataclasses.py +0 -0
  83. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/datetime.py +0 -0
  84. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/difflib.py +0 -0
  85. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/disk_cache.py +0 -0
  86. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/entrypoints/info.py +0 -0
  87. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/entrypoints/safe_rmdir.py +0 -0
  88. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/entrypoints/tree.py +0 -0
  89. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/enum.py +0 -0
  90. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/functools.py +0 -0
  91. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/hashlib.py +0 -0
  92. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/importlib.py +0 -0
  93. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/inspect.py +0 -0
  94. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/json.py +0 -0
  95. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/jsonl.py +0 -0
  96. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/math.py +0 -0
  97. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/os.py +0 -0
  98. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/pickle.py +0 -0
  99. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/random.py +0 -0
  100. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/re.py +0 -0
  101. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/semver.py +0 -0
  102. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/serialization/__init__.py +0 -0
  103. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/time.py +0 -0
  104. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/typing/__init__.py +0 -0
  105. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/typing/checks.py +0 -0
  106. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/src/pythonwrench/typing/classes.py +0 -0
  107. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_abc.py +0 -0
  108. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_cast.py +0 -0
  109. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_checksum.py +0 -0
  110. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_collections.py +0 -0
  111. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_core.py +0 -0
  112. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_csv.py +0 -0
  113. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_dataclasses.py +0 -0
  114. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_difflib.py +0 -0
  115. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_disk_cache.py +0 -0
  116. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_entrypoints.py +0 -0
  117. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_enum.py +0 -0
  118. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_functools.py +0 -0
  119. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_hashlib.py +0 -0
  120. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_importlib.py +0 -0
  121. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_inspect.py +0 -0
  122. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_json.py +0 -0
  123. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_jsonl.py +0 -0
  124. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_logging.py +0 -0
  125. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_math.py +0 -0
  126. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_os.py +0 -0
  127. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_random.py +0 -0
  128. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_readme.py +0 -0
  129. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_semver.py +0 -0
  130. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/tests/test_typing.py +0 -0
  131. {pythonwrench-0.6.6 → pythonwrench-0.6.7}/uv.lock +0 -0
@@ -2,10 +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
+
5
14
  ## [0.6.6] 2026-08-20
6
15
  ### Fixed
7
16
  - `setup_logging_level` isinstance check.
8
17
 
18
+
9
19
  ## [0.6.5] 2026-08-20
10
20
  ### Added
11
21
  - `return_single_namespace` option for `parse_args_using_dataclass` function.
@@ -17,5 +17,5 @@ keywords:
17
17
  - tools
18
18
  - utilities
19
19
  license: MIT
20
- version: 0.6.6
21
- date-released: '2026-08-20'
20
+ version: 0.6.7
21
+ date-released: '2026-09-07'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pythonwrench
3
- Version: 0.6.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/
@@ -9,7 +9,7 @@ __author_email__ = "labbeti.pub@gmail.com"
9
9
  __license__ = "MIT"
10
10
  __maintainer__ = "Étienne Labbé (Labbeti)"
11
11
  __status__ = "Development"
12
- __version__ = "0.6.6"
12
+ __version__ = "0.6.7"
13
13
 
14
14
 
15
15
  from typing import TYPE_CHECKING
@@ -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"--{field.name}"]
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"--{dashed_arg_name}")
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:
@@ -148,7 +148,7 @@ def setup_logging_level(
148
148
 
149
149
  try:
150
150
  index = streams.index(handler.stream)
151
- except IndexError:
151
+ except ValueError:
152
152
  continue
153
153
 
154
154
  handler.setFormatter(fmt)
@@ -5,10 +5,13 @@ import os
5
5
  from io import TextIOWrapper
6
6
  from pathlib import Path
7
7
  from typing import (
8
+ Literal,
8
9
  Union,
9
10
  overload,
10
11
  )
11
12
 
13
+ OnError = Literal["raise", "default"]
14
+
12
15
 
13
16
  @overload
14
17
  def _setup_output_fpath(
@@ -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(file, **json_loads_kwds)
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(content: str, /, **json_loads_kwds) -> Any:
165
- """Load s json."""
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(buffer, **json_loads_kwds)
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(buffer: TextIOBase, **json_loads_kwds) -> Any:
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
- return json.load(buffer, **json_loads_kwds)
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(content: str, /, **json_loads_kwds) -> list:
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(buffer, **json_loads_kwds)
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(buffer: TextIOBase, **json_loads_kwds) -> list:
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(content, **json_loads_kwds)
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 typing import Any, BinaryIO, Union
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(file: Union[str, Path, BinaryIO], /, **pkl_loads_kwds) -> Any:
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(content: bytes, /, **pkl_loads_kwds) -> Any:
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(buffer, **pkl_loads_kwds)
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(buffer: BinaryIO, **pkl_loads_kwds) -> Any:
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
- return pickle.load(buffer, **pkl_loads_kwds)
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
- """Perform the warn once operation."""
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
- """Perform the warn once operation."""
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()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes