pythonwrench 0.4.0__tar.gz → 0.4.2__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 (73) hide show
  1. {pythonwrench-0.4.0/src/pythonwrench.egg-info → pythonwrench-0.4.2}/PKG-INFO +3 -3
  2. pythonwrench-0.4.2/docs/requirements.txt +3 -0
  3. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/pyproject.toml +4 -1
  4. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/__init__.py +6 -2
  5. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/_core.py +63 -5
  6. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/abc.py +4 -3
  7. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/argparse.py +9 -9
  8. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/cast.py +3 -4
  9. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/checksum.py +6 -5
  10. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/collections/collections.py +31 -36
  11. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/csv.py +9 -9
  12. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/disk_cache.py +77 -47
  13. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/functools.py +25 -10
  14. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/importlib.py +9 -5
  15. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/inspect.py +28 -25
  16. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/json.py +3 -3
  17. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/jsonl.py +3 -3
  18. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/pickle.py +3 -3
  19. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/random.py +12 -1
  20. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/re.py +3 -4
  21. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/semver.py +2 -0
  22. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/typing/__init__.py +2 -0
  23. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/typing/checks.py +13 -11
  24. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/typing/classes.py +30 -18
  25. {pythonwrench-0.4.0 → pythonwrench-0.4.2/src/pythonwrench.egg-info}/PKG-INFO +3 -3
  26. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench.egg-info/SOURCES.txt +2 -1
  27. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench.egg-info/requires.txt +2 -0
  28. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_checksum.py +6 -0
  29. pythonwrench-0.4.2/tests/test_functools.py +32 -0
  30. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_inspect.py +17 -0
  31. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_typing.py +7 -0
  32. pythonwrench-0.4.0/src/pythonwrench/io.py +0 -59
  33. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/LICENSE +0 -0
  34. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/README.md +0 -0
  35. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/setup.cfg +0 -0
  36. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/setup.py +0 -0
  37. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/__main__.py +0 -0
  38. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/collections/__init__.py +0 -0
  39. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/collections/prop.py +0 -0
  40. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/collections/reducers.py +0 -0
  41. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/dataclasses.py +0 -0
  42. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/datetime.py +0 -0
  43. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/difflib.py +0 -0
  44. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/entries.py +0 -0
  45. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/enum.py +0 -0
  46. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/hashlib.py +0 -0
  47. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/logging.py +0 -0
  48. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/math.py +0 -0
  49. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/os.py +0 -0
  50. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench/warnings.py +0 -0
  51. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
  52. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench.egg-info/entry_points.txt +0 -0
  53. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/src/pythonwrench.egg-info/top_level.txt +0 -0
  54. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_abc.py +0 -0
  55. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_argparse.py +0 -0
  56. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_cast.py +0 -0
  57. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_collections.py +0 -0
  58. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_csv.py +0 -0
  59. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_dataclasses.py +0 -0
  60. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_difflib.py +0 -0
  61. /pythonwrench-0.4.0/tests/test_functools.py → /pythonwrench-0.4.2/tests/test_disk_cache.py +0 -0
  62. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_entries.py +0 -0
  63. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_enum.py +0 -0
  64. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_hashlib.py +0 -0
  65. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_importlib.py +0 -0
  66. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_json.py +0 -0
  67. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_jsonl.py +0 -0
  68. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_logging.py +0 -0
  69. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_math.py +0 -0
  70. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_os.py +0 -0
  71. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_random.py +0 -0
  72. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_readme.py +0 -0
  73. {pythonwrench-0.4.0 → pythonwrench-0.4.2}/tests/test_semver.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: pythonwrench
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Set of tools for Python that could be in the standard library.
5
5
  Author-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
6
6
  Maintainer-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
@@ -47,7 +47,7 @@ Requires-Python: >=3.8
47
47
  Description-Content-Type: text/markdown
48
48
  License-File: LICENSE
49
49
  Requires-Dist: typing-extensions>=4.10.0
50
- Dynamic: license-file
50
+ Provides-Extra: dev
51
51
 
52
52
  # pythonwrench
53
53
 
@@ -0,0 +1,3 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ sphinx-immaterial>=0.11.14
@@ -45,6 +45,9 @@ pyw-info = "pythonwrench.entries:print_install_info"
45
45
  pyw-tree = "pythonwrench.entries:main_tree"
46
46
  pyw-safe-rmdir = "pythonwrench.entries:main_safe_rmdir"
47
47
 
48
+ [project.optional-dependencies]
49
+ dev = []
50
+
48
51
  [build-system]
49
52
  requires = ["setuptools >= 61.0"]
50
53
  build-backend = "setuptools.build_meta"
@@ -87,6 +90,6 @@ dev = [
87
90
  "ruff>=0.12.0",
88
91
  "setuptools",
89
92
  "sphinx",
90
- "sphinx-press-theme",
93
+ "sphinx-immaterial>=0.11.14",
91
94
  "twine",
92
95
  ]
@@ -9,25 +9,28 @@ __author_email__ = "labbeti.pub@gmail.com"
9
9
  __license__ = "MIT"
10
10
  __maintainer__ = "Étienne Labbé (Labbeti)"
11
11
  __status__ = "Development"
12
- __version__ = "0.4.0"
12
+ __version__ = "0.4.2"
13
13
 
14
14
 
15
15
  # Re-import for language servers
16
16
  from . import abc as abc
17
17
  from . import argparse as argparse
18
+ from . import cast as cast
19
+ from . import checksum as checksum
18
20
  from . import collections as collections
19
21
  from . import csv as csv
20
22
  from . import dataclasses as dataclasses
21
23
  from . import datetime as datetime
22
24
  from . import difflib as difflib
25
+ from . import disk_cache as disk_cache
23
26
  from . import entries as entries
24
27
  from . import enum as enum
25
28
  from . import functools as functools
26
29
  from . import hashlib as hashlib
27
30
  from . import importlib as importlib
28
31
  from . import inspect as inspect
29
- from . import io as io
30
32
  from . import json as json
33
+ from . import jsonl as jsonl
31
34
  from . import logging as logging
32
35
  from . import math as math
33
36
  from . import os as os
@@ -166,6 +169,7 @@ from .typing import (
166
169
  SupportsAnd,
167
170
  SupportsBool,
168
171
  SupportsDiv,
172
+ SupportsGetitem,
169
173
  SupportsGetitemIterLen,
170
174
  SupportsGetitemLen,
171
175
  SupportsIterLen,
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
+ import os
4
5
  from functools import wraps
6
+ from io import TextIOWrapper
7
+ from pathlib import Path
5
8
  from typing import (
6
9
  Any,
7
10
  Callable,
@@ -9,22 +12,27 @@ from typing import (
9
12
  Generic,
10
13
  Literal,
11
14
  Optional,
15
+ Protocol,
12
16
  Tuple,
13
- TypeVar,
14
17
  Union,
15
18
  get_args,
19
+ overload,
16
20
  )
17
21
 
18
- from typing_extensions import ParamSpec
22
+ from typing_extensions import ParamSpec, TypeVar
19
23
 
20
24
  P = ParamSpec("P")
21
- T = TypeVar("T")
22
- U = TypeVar("U")
25
+ T = TypeVar("T", covariant=True)
26
+ U = TypeVar("U", covariant=True)
23
27
  T_Output = TypeVar("T_Output")
28
+ T_Any = TypeVar("T_Any", contravariant=True, default=Any)
24
29
 
25
30
  UnkMode = Literal["identity", "error"]
26
31
  ClassOrTuple = Union[type, Tuple[type, ...]]
27
- Predicate = Callable[[Any], bool]
32
+
33
+
34
+ class Predicate(Protocol[T_Any]):
35
+ def __call__(self, x: T_Any) -> bool: ...
28
36
 
29
37
 
30
38
  def return_none(*args, **kwargs) -> None:
@@ -161,3 +169,53 @@ class _FunctionRegistry(Generic[T_Output]):
161
169
  else:
162
170
  msg = f"Invalid argument {unk_mode=}. (expected one of {get_args(UnkMode)})"
163
171
  raise ValueError(msg)
172
+
173
+
174
+ @overload
175
+ def _setup_output_fpath(
176
+ fpath: Union[str, Path, os.PathLike],
177
+ overwrite: bool,
178
+ make_parents: bool,
179
+ absolute: bool = True,
180
+ ) -> Path: ...
181
+
182
+
183
+ @overload
184
+ def _setup_output_fpath(
185
+ fpath: TextIOWrapper,
186
+ overwrite: bool,
187
+ make_parents: bool,
188
+ absolute: bool = True,
189
+ ) -> TextIOWrapper: ...
190
+
191
+
192
+ @overload
193
+ def _setup_output_fpath(
194
+ fpath: None,
195
+ overwrite: bool,
196
+ make_parents: bool,
197
+ absolute: bool = True,
198
+ ) -> None: ...
199
+
200
+
201
+ def _setup_output_fpath(
202
+ fpath: Union[str, Path, os.PathLike, TextIOWrapper, None],
203
+ overwrite: bool,
204
+ make_parents: bool,
205
+ absolute: bool = True,
206
+ ) -> Union[Path, None, TextIOWrapper]:
207
+ """Resolve path, expand path and create intermediate parents."""
208
+ if not isinstance(fpath, (str, Path, os.PathLike)):
209
+ return fpath
210
+
211
+ fpath = Path(fpath)
212
+ if absolute:
213
+ fpath = fpath.resolve().expanduser()
214
+
215
+ if not overwrite and fpath.exists():
216
+ msg = f"File {fpath} already exists."
217
+ raise FileExistsError(msg)
218
+ elif make_parents:
219
+ fpath.parent.mkdir(parents=True, exist_ok=True)
220
+
221
+ return fpath
@@ -7,14 +7,15 @@ from typing import Any, ClassVar, Dict, Type
7
7
  class Singleton(type):
8
8
  """Singleton metaclass.
9
9
 
10
- To use it, just inherit from metaclass:
11
- ```
10
+ To use it, just inherit from metaclass.
11
+
12
+ Example
13
+ -------
12
14
  >>> class MyClass(metaclass=Singleton):
13
15
  >>> pass
14
16
  >>> a1 = MyClass()
15
17
  >>> a2 = MyClass()
16
18
  >>> # a1 and a2 are exactly the same instance, i.e. id(a1) == id(a2)
17
- ```
18
19
  """
19
20
 
20
21
  _instances: ClassVar[Dict[Type, Any]] = {}
@@ -35,7 +35,7 @@ def parse_to(
35
35
  ) -> Callable[[str], T]:
36
36
  """Returns a callable that convert string value to target type safely.
37
37
 
38
- Intended for argparse boolean arguments.
38
+ Intended for argparse arguments.
39
39
  """
40
40
  return partial(
41
41
  str_to_type,
@@ -56,7 +56,7 @@ def str_to_type(
56
56
  false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
57
57
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
58
58
  ) -> T:
59
- """Convert string values to target type safely. Intended for argparse boolean arguments.
59
+ """Convert string values to target type safely. Intended for argparse arguments.
60
60
 
61
61
  - True values: 'True', 'T', 'yes', 'y', '1'.
62
62
  - False values: 'False', 'F', 'no', 'n', '0'.
@@ -84,7 +84,7 @@ def str_to_bool(
84
84
  true_values: Union[str, Iterable[str]] = DEFAULT_TRUE_VALUES,
85
85
  false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
86
86
  ) -> bool:
87
- """Convert string values to bool safely. Intended for argparse boolean arguments.
87
+ """Convert string values to bool safely. Intended for argparse arguments.
88
88
 
89
89
  - True values: 'True', 'T', 'yes', 'y', '1'.
90
90
  - False values: 'False', 'F', 'no', 'n', '0'.
@@ -105,7 +105,7 @@ def str_to_none(
105
105
  case_sensitive: bool = False,
106
106
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
107
107
  ) -> None:
108
- """Convert string values to None safely. Intended for argparse boolean arguments.
108
+ """Convert string values to None safely. Intended for argparse arguments.
109
109
 
110
110
  - None values: 'None', 'null'
111
111
  - Other raises ValueError.
@@ -123,7 +123,7 @@ def str_to_optional_bool(
123
123
  false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
124
124
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
125
125
  ) -> Optional[bool]:
126
- """Convert string values to optional bool safely. Intended for argparse boolean arguments.
126
+ """Convert string values to optional bool safely. Intended for argparse arguments.
127
127
 
128
128
  - True values: 'True', 'T', 'yes', 'y', '1'.
129
129
  - False values: 'False', 'F', 'no', 'n', '0'.
@@ -141,7 +141,7 @@ def str_to_optional_float(
141
141
  case_sensitive: bool = False,
142
142
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
143
143
  ) -> Optional[float]:
144
- """Convert string values to optional float safely. Intended for argparse boolean arguments."""
144
+ """Convert string values to optional float safely. Intended for argparse arguments."""
145
145
  return str_to_type(
146
146
  x, Optional[float], case_sensitive=case_sensitive, none_values=none_values
147
147
  )
@@ -153,7 +153,7 @@ def str_to_optional_int(
153
153
  case_sensitive: bool = False,
154
154
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
155
155
  ) -> Optional[int]:
156
- """Convert string values to optional int safely. Intended for argparse boolean arguments."""
156
+ """Convert string values to optional int safely. Intended for argparse arguments."""
157
157
  return str_to_type(
158
158
  x, Optional[int], case_sensitive=case_sensitive, none_values=none_values
159
159
  )
@@ -165,7 +165,7 @@ def str_to_optional_str(
165
165
  case_sensitive: bool = False,
166
166
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
167
167
  ) -> Optional[str]:
168
- """Convert string values to optional str safely. Intended for argparse boolean arguments."""
168
+ """Convert string values to optional str safely. Intended for argparse arguments."""
169
169
  return str_to_type(
170
170
  x, Optional[str], case_sensitive=case_sensitive, none_values=none_values
171
171
  )
@@ -284,7 +284,7 @@ def _str_to_none_impl(
284
284
  case_sensitive: bool = False,
285
285
  none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
286
286
  ) -> Union[None, Exception]:
287
- """Convert string values to None safely. Intended for argparse boolean arguments.
287
+ """Convert string values to None safely. Intended for argparse arguments.
288
288
 
289
289
  - None values: 'None', 'null'
290
290
  - Other raises ValueError.
@@ -63,16 +63,15 @@ def register_as_builtin_fn(
63
63
  priority: int = 0,
64
64
  ) -> Callable:
65
65
  """Decorator to add an as_builtin function.
66
- ```
67
- >>> import numpy as np
68
66
 
67
+ Example
68
+ -------
69
+ >>> import numpy as np
69
70
  >>> @register_as_builtin_fn(np.ndarray)
70
71
  >>> def my_checksum_for_numpy(x: np.ndarray):
71
72
  >>> return x.tolist()
72
-
73
73
  >>> pw.as_builtin([np.array([1, 2]), [3, 4]])
74
74
  ... [[1, 2], [3, 4]]
75
- ```
76
75
  """
77
76
  return _AS_BUILTIN_REGISTRY.register_decorator(
78
77
  class_or_tuple,
@@ -60,15 +60,14 @@ def register_checksum_fn(
60
60
  priority: int = 0,
61
61
  ) -> Callable:
62
62
  """Decorator to add a checksum function.
63
- ```
64
- >>> import numpy as np
65
63
 
64
+ Example
65
+ -------
66
+ >>> import numpy as np
66
67
  >>> @register_checksum_fn(np.ndarray)
67
68
  >>> def my_checksum_for_numpy(x: np.ndarray):
68
69
  >>> return int(x.sum())
69
-
70
70
  >>> pw.checksum_any(np.array([1, 2])) # calls my_checksum_for_numpy internally, even if array in nested inside a list, dict, etc.
71
- ```
72
71
  """
73
72
  return _CHECKSUM_REGISTRY.register_decorator(
74
73
  class_or_tuple,
@@ -196,7 +195,9 @@ def checksum_set(x: Union[set, frozenset], **kwargs) -> int:
196
195
  kwargs["accumulator"] = kwargs.get("accumulator", 0) + _cached_checksum_str(
197
196
  get_fullname(x)
198
197
  )
199
- return _checksum_iterable(sorted(x), **kwargs)
198
+ # Simply use sum here, order does not matter
199
+ csum = sum(checksum_any(xi, **kwargs) for xi in x)
200
+ return csum
200
201
 
201
202
 
202
203
  @register_checksum_fn(range)
@@ -74,17 +74,17 @@ def contained(
74
74
  @overload
75
75
  def dict_list_to_list_dict(
76
76
  dic: Mapping[T, Iterable[U]],
77
- key_mode: Literal["same", "intersect"],
78
- default_val: Any = None,
79
- ) -> List[Dict[T, U]]: ...
77
+ key_mode: Literal["union"] = "union",
78
+ default_val: W = None,
79
+ ) -> List[Dict[T, Union[U, W]]]: ...
80
80
 
81
81
 
82
82
  @overload
83
83
  def dict_list_to_list_dict(
84
84
  dic: Mapping[T, Iterable[U]],
85
- key_mode: Literal["union"] = "union",
86
- default_val: W = None,
87
- ) -> List[Dict[T, Union[U, W]]]: ...
85
+ key_mode: Literal["same", "intersect"],
86
+ default_val: Any = None,
87
+ ) -> List[Dict[T, U]]: ...
88
88
 
89
89
 
90
90
  def dict_list_to_list_dict(
@@ -95,20 +95,16 @@ def dict_list_to_list_dict(
95
95
  """Convert dict of lists with same sizes to list of dicts.
96
96
 
97
97
  Example 1
98
- ----------
99
- ```
98
+ ---------
100
99
  >>> dic = {"a": [1, 2], "b": [3, 4]}
101
100
  >>> dict_list_to_list_dict(dic)
102
101
  ... [{"a": 1, "b": 3}, {"a": 2, "b": 4}]
103
- ```
104
102
 
105
103
  Example 2
106
- ----------
107
- ```
104
+ ---------
108
105
  >>> dic = {"a": [1, 2, 3], "b": [4], "c": [5, 6]}
109
106
  >>> dict_list_to_list_dict(dic, key_mode="union", default=-1)
110
107
  ... [{"a": 1, "b": 4, "c": 5}, {"a": 2, "b": -1, "c": 6}, {"a": 3, "b": -1, "c": -1}]
111
- ```
112
108
  """
113
109
  if len(dic) == 0:
114
110
  return []
@@ -151,11 +147,9 @@ def dump_dict(
151
147
 
152
148
  Example 1:
153
149
  ----------
154
- ```
155
150
  >>> d = {"a": 1, "b": 2}
156
151
  >>> dump_dict(d)
157
152
  ... 'a=1, b=2'
158
- ```
159
153
  """
160
154
  if dic is None:
161
155
  dic = {}
@@ -360,7 +354,6 @@ def flat_dict_of_dict(
360
354
 
361
355
  Example 1
362
356
  ---------
363
- ```
364
357
  >>> dic = {
365
358
  ... "a": 1,
366
359
  ... "b": {
@@ -370,15 +363,12 @@ def flat_dict_of_dict(
370
363
  ... }
371
364
  >>> flat_dict_of_dict(dic)
372
365
  ... {"a": 1, "b.a": 2, "b.b": 10}
373
- ```
374
366
 
375
367
  Example 2
376
368
  ---------
377
- ```
378
369
  >>> dic = {"a": ["hello", "world"], "b": 3}
379
370
  >>> flat_dict_of_dict(dic, flat_iterables=True)
380
371
  ... {"a.0": "hello", "a.1": "world", "b": 3}
381
- ```
382
372
 
383
373
  Args:
384
374
  nested_dic: Nested mapping containing sub-mappings or iterables.
@@ -508,10 +498,11 @@ def list_dict_to_dict_list(
508
498
 
509
499
  Args:
510
500
  lst: The list of dict to merge.
511
- key_mode: Can be "same" or "intersect".
512
- If "same", all the dictionaries must contains the same keys otherwise a ValueError will be raised.
513
- If "intersect", only the intersection of all keys will be used in output.
514
- If "union", the output dict will contains the union of all keys, and the missing value will use the argument default_val.
501
+ key_mode: Can be "same" or "intersect". \
502
+ - If "same", all the dictionaries must contains the same keys otherwise a ValueError will be raised. \
503
+ - If "intersect", only the intersection of all keys will be used in output. \
504
+ - If "union", the output dict will contains the union of all keys, and the missing value will use the argument default_val. \
505
+ - If an iterable of elements, use them as keys for output dict.
515
506
  default_val: Default value of an element when key_mode is "union". defaults to None.
516
507
  default_val_fn: Function to return the default value according to a specific key. defaults to None.
517
508
  list_fn: Optional function to build the values. defaults to identity.
@@ -622,7 +613,6 @@ def unflat_dict_of_dict(dic: Mapping[str, Any], *, sep: str = ".") -> Dict[str,
622
613
 
623
614
  Example 1
624
615
  ----------
625
- ```
626
616
  >>> dic = {
627
617
  "a.a": 1,
628
618
  "b.a": 2,
@@ -631,7 +621,6 @@ def unflat_dict_of_dict(dic: Mapping[str, Any], *, sep: str = ".") -> Dict[str,
631
621
  }
632
622
  >>> unflat_dict_of_dict(dic)
633
623
  ... {"a": {"a": 1}, "b": {"a": 2, "b": 3}, "c": 4}
634
- ```
635
624
  """
636
625
  output = {}
637
626
  for k, v in dic.items():
@@ -671,6 +660,7 @@ def unflat_list_of_list(
671
660
 
672
661
 
673
662
  def union_dicts(dicts: Iterable[Dict[K, V]]) -> Dict[K, V]:
663
+ """Performs union of dictionaries."""
674
664
  if Version.python() >= Version("3.9.0"):
675
665
  return reduce_or(*dicts)
676
666
 
@@ -721,19 +711,24 @@ def unzip(
721
711
  ) -> Tuple[List[T], List[U], List[V], List[W], List[X]]: ...
722
712
 
723
713
 
714
+ @overload
715
+ def unzip(
716
+ lst: Iterable[Tuple[T, ...]],
717
+ ) -> Tuple[List[T], ...]: ...
718
+
719
+
724
720
  def unzip(lst):
725
721
  """Invert function of builtin zip().
726
722
 
727
- .. code-block:: python
728
- :caption: Example
729
-
730
- >>> lst1 = [1, 2, 3, 4]
731
- >>> lst2 = [5, 6, 7, 8]
732
- >>> lst_zipped = list(zip(lst1, lst2))
733
- >>> lst_zipped
734
- ... [(1, 5), (2, 6), (3, 7), (4, 8)]
735
- >>> unzip(lst_zipped)
736
- ... [1, 2, 3, 4], [5, 6, 7, 8]
723
+ Example
724
+ -------
725
+ >>> lst1 = [1, 2, 3, 4]
726
+ >>> lst2 = [5, 6, 7, 8]
727
+ >>> zipped_list = list(zip(lst1, lst2))
728
+ >>> zipped_list
729
+ ... [(1, 5), (2, 6), (3, 7), (4, 8)]
730
+ >>> unzip(zipped_list)
731
+ ... [1, 2, 3, 4], [5, 6, 7, 8]
737
732
  """
738
733
  return tuple(map(list, zip(*lst)))
739
734
 
@@ -741,8 +736,8 @@ def unzip(lst):
741
736
  def duplicate_list(lst: List[T], sizes: List[int]) -> List[T]:
742
737
  """Duplicate elements elements of a list with the corresponding sizes.
743
738
 
744
- Example 1
745
- ----------
739
+ Example
740
+ -------
746
741
  >>> lst = ["a", "b", "c", "d", "e"]
747
742
  >>> sizes = [1, 0, 2, 1, 3]
748
743
  >>> duplicate_list(lst, sizes)
@@ -21,10 +21,10 @@ from typing import (
21
21
  overload,
22
22
  )
23
23
 
24
+ from pythonwrench._core import _setup_output_fpath
24
25
  from pythonwrench.cast import as_builtin
25
26
  from pythonwrench.collections import dict_list_to_list_dict, list_dict_to_dict_list
26
27
  from pythonwrench.functools import function_alias
27
- from pythonwrench.io import _setup_output_fpath
28
28
  from pythonwrench.typing import isinstance_generic
29
29
 
30
30
  T = TypeVar("T")
@@ -48,7 +48,7 @@ def dump_csv(
48
48
  replace_newline_by: Optional[str] = "\\n",
49
49
  **csv_writer_kwds,
50
50
  ) -> str:
51
- """Dump content to CSV format into string and/or file.
51
+ r"""Dump content to CSV format into string and/or file.
52
52
 
53
53
  Args:
54
54
  data: Data to serialize. Can be a list of dicts, dicts of lists or list of lists.
@@ -59,7 +59,7 @@ def dump_csv(
59
59
  header: Indicates if CSV must have header. If "auto", an header is added when a dict of list or list of dicts is passed. defaults to "auto".
60
60
  align_content: If True, center content at the middle of each row for better visualization. defaults to False.
61
61
  replace_newline_by: Replace newline character to avoid newline in CSV content. defaults to "\\n".
62
- **csv_writer_kwds: Others optional arguments passed to CSV writer object.
62
+ \*\*csv_writer_kwds: Others optional arguments passed to CSV writer object.
63
63
 
64
64
  Returns:
65
65
  Dumped content as string.
@@ -98,7 +98,7 @@ def dumps_csv(
98
98
  replace_newline_by: Optional[str] = "\\n",
99
99
  **csv_writer_kwds,
100
100
  ) -> str:
101
- """Dump content to CSV format into string.
101
+ r"""Dump content to CSV format into string.
102
102
 
103
103
  Args:
104
104
  data: Data to serialize. Can be a list of dicts, dicts of lists or list of lists.
@@ -108,7 +108,7 @@ def dumps_csv(
108
108
  header: Indicates if CSV must have header. If "auto", an header is added when a dict of list or list of dicts is passed. defaults to "auto".
109
109
  align_content: If True, center content at the middle of each row for better visualization. defaults to False.
110
110
  replace_newline_by: Replace newline character to avoid newline in CSV content. defaults to "\\n".
111
- **csv_writer_kwds: Others optional arguments passed to CSV writer object.
111
+ \*\*csv_writer_kwds: Others optional arguments passed to CSV writer object.
112
112
 
113
113
  Returns:
114
114
  Dumped content as string.
@@ -140,7 +140,7 @@ def save_csv(
140
140
  replace_newline_by: Optional[str] = "\\n",
141
141
  **csv_writer_kwds,
142
142
  ) -> None:
143
- """Save content to CSV format into a file or buffer.
143
+ r"""Save content to CSV format into a file or buffer.
144
144
 
145
145
  Args:
146
146
  data: Data to serialize. Can be a list of dicts, dicts of lists or list of lists.
@@ -150,7 +150,7 @@ def save_csv(
150
150
  header: Indicates if CSV must have header. If "auto", an header is added when a dict of list or list of dicts is passed. defaults to "auto".
151
151
  align_content: If True, center content at the middle of each row for better visualization. defaults to False.
152
152
  replace_newline_by: Replace newline character to avoid newline in CSV content. defaults to "\\n".
153
- **csv_writer_kwds: Others optional arguments passed to CSV writer object.
153
+ \*\*csv_writer_kwds: Others optional arguments passed to CSV writer object.
154
154
  """
155
155
  if isinstance(file, (str, Path, PathLike)):
156
156
  file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
@@ -320,14 +320,14 @@ def load_csv(
320
320
  delimiter: Optional[str] = ",",
321
321
  **csv_reader_kwds,
322
322
  ) -> Union[List[Dict[str, Any]], Dict[str, List[Any]]]:
323
- """Load content from csv filepath.
323
+ r"""Load content from csv filepath.
324
324
 
325
325
  Args:
326
326
  orient: Orientation of the output value. Can be "list" or "dict". defaults to "list".
327
327
  header: Specify if CSV has header column. defaults to True.
328
328
  comment_start: If this string is not None and a line starts with this string, the line will be ignored. defaults to None.
329
329
  delimiter: Value delimiter. defaults to ",".
330
- **csv_reader_kwds: Other optional csv arguments.
330
+ \*\*csv_reader_kwds: Other optional csv arguments.
331
331
 
332
332
  Returns:
333
333
  The loaded values as dict of lists, list of dicts or list of lists.