omlish 0.0.0.dev1__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (187) hide show
  1. omlish/__about__.py +7 -0
  2. omlish/__init__.py +0 -0
  3. omlish/argparse.py +223 -0
  4. omlish/asyncs/__init__.py +17 -0
  5. omlish/asyncs/anyio.py +23 -0
  6. omlish/asyncs/asyncio.py +19 -0
  7. omlish/asyncs/asyncs.py +76 -0
  8. omlish/asyncs/futures.py +179 -0
  9. omlish/asyncs/trio.py +11 -0
  10. omlish/c3.py +173 -0
  11. omlish/cached.py +9 -0
  12. omlish/check.py +231 -0
  13. omlish/collections/__init__.py +63 -0
  14. omlish/collections/_abc.py +156 -0
  15. omlish/collections/_io_abc.py +78 -0
  16. omlish/collections/cache/__init__.py +11 -0
  17. omlish/collections/cache/descriptor.py +188 -0
  18. omlish/collections/cache/impl.py +485 -0
  19. omlish/collections/cache/types.py +37 -0
  20. omlish/collections/coerce.py +337 -0
  21. omlish/collections/frozen.py +148 -0
  22. omlish/collections/identity.py +106 -0
  23. omlish/collections/indexed.py +75 -0
  24. omlish/collections/mappings.py +127 -0
  25. omlish/collections/ordered.py +81 -0
  26. omlish/collections/persistent.py +36 -0
  27. omlish/collections/skiplist.py +193 -0
  28. omlish/collections/sorted.py +126 -0
  29. omlish/collections/treap.py +228 -0
  30. omlish/collections/treapmap.py +144 -0
  31. omlish/collections/unmodifiable.py +174 -0
  32. omlish/collections/utils.py +110 -0
  33. omlish/configs/__init__.py +0 -0
  34. omlish/configs/flattening.py +147 -0
  35. omlish/configs/props.py +64 -0
  36. omlish/dataclasses/__init__.py +83 -0
  37. omlish/dataclasses/impl/__init__.py +6 -0
  38. omlish/dataclasses/impl/api.py +260 -0
  39. omlish/dataclasses/impl/as_.py +76 -0
  40. omlish/dataclasses/impl/exceptions.py +2 -0
  41. omlish/dataclasses/impl/fields.py +148 -0
  42. omlish/dataclasses/impl/frozen.py +55 -0
  43. omlish/dataclasses/impl/hashing.py +85 -0
  44. omlish/dataclasses/impl/init.py +173 -0
  45. omlish/dataclasses/impl/internals.py +118 -0
  46. omlish/dataclasses/impl/main.py +150 -0
  47. omlish/dataclasses/impl/metaclass.py +126 -0
  48. omlish/dataclasses/impl/metadata.py +74 -0
  49. omlish/dataclasses/impl/order.py +47 -0
  50. omlish/dataclasses/impl/params.py +150 -0
  51. omlish/dataclasses/impl/processing.py +16 -0
  52. omlish/dataclasses/impl/reflect.py +173 -0
  53. omlish/dataclasses/impl/replace.py +40 -0
  54. omlish/dataclasses/impl/repr.py +34 -0
  55. omlish/dataclasses/impl/simple.py +92 -0
  56. omlish/dataclasses/impl/slots.py +80 -0
  57. omlish/dataclasses/impl/utils.py +167 -0
  58. omlish/defs.py +193 -0
  59. omlish/dispatch/__init__.py +3 -0
  60. omlish/dispatch/dispatch.py +137 -0
  61. omlish/dispatch/functions.py +52 -0
  62. omlish/dispatch/methods.py +162 -0
  63. omlish/docker.py +149 -0
  64. omlish/dynamic.py +220 -0
  65. omlish/graphs/__init__.py +0 -0
  66. omlish/graphs/dot/__init__.py +19 -0
  67. omlish/graphs/dot/items.py +162 -0
  68. omlish/graphs/dot/rendering.py +147 -0
  69. omlish/graphs/dot/utils.py +30 -0
  70. omlish/graphs/trees.py +249 -0
  71. omlish/http/__init__.py +0 -0
  72. omlish/http/consts.py +20 -0
  73. omlish/http/wsgi.py +34 -0
  74. omlish/inject/__init__.py +85 -0
  75. omlish/inject/binder.py +12 -0
  76. omlish/inject/bindings.py +49 -0
  77. omlish/inject/eagers.py +21 -0
  78. omlish/inject/elements.py +43 -0
  79. omlish/inject/exceptions.py +49 -0
  80. omlish/inject/impl/__init__.py +0 -0
  81. omlish/inject/impl/bindings.py +19 -0
  82. omlish/inject/impl/elements.py +154 -0
  83. omlish/inject/impl/injector.py +182 -0
  84. omlish/inject/impl/inspect.py +98 -0
  85. omlish/inject/impl/private.py +109 -0
  86. omlish/inject/impl/providers.py +132 -0
  87. omlish/inject/impl/scopes.py +198 -0
  88. omlish/inject/injector.py +40 -0
  89. omlish/inject/inspect.py +14 -0
  90. omlish/inject/keys.py +43 -0
  91. omlish/inject/managed.py +24 -0
  92. omlish/inject/overrides.py +18 -0
  93. omlish/inject/private.py +29 -0
  94. omlish/inject/providers.py +111 -0
  95. omlish/inject/proxy.py +48 -0
  96. omlish/inject/scopes.py +84 -0
  97. omlish/inject/types.py +21 -0
  98. omlish/iterators.py +184 -0
  99. omlish/json.py +194 -0
  100. omlish/lang/__init__.py +112 -0
  101. omlish/lang/cached.py +267 -0
  102. omlish/lang/classes/__init__.py +24 -0
  103. omlish/lang/classes/abstract.py +74 -0
  104. omlish/lang/classes/restrict.py +137 -0
  105. omlish/lang/classes/simple.py +120 -0
  106. omlish/lang/classes/test/__init__.py +0 -0
  107. omlish/lang/classes/test/test_abstract.py +89 -0
  108. omlish/lang/classes/test/test_restrict.py +71 -0
  109. omlish/lang/classes/test/test_simple.py +58 -0
  110. omlish/lang/classes/test/test_virtual.py +72 -0
  111. omlish/lang/classes/virtual.py +130 -0
  112. omlish/lang/clsdct.py +67 -0
  113. omlish/lang/cmp.py +63 -0
  114. omlish/lang/contextmanagers.py +249 -0
  115. omlish/lang/datetimes.py +67 -0
  116. omlish/lang/descriptors.py +52 -0
  117. omlish/lang/functions.py +126 -0
  118. omlish/lang/imports.py +153 -0
  119. omlish/lang/iterables.py +54 -0
  120. omlish/lang/maybes.py +136 -0
  121. omlish/lang/objects.py +103 -0
  122. omlish/lang/resolving.py +50 -0
  123. omlish/lang/strings.py +128 -0
  124. omlish/lang/typing.py +92 -0
  125. omlish/libc.py +532 -0
  126. omlish/logs/__init__.py +9 -0
  127. omlish/logs/_abc.py +247 -0
  128. omlish/logs/configs.py +62 -0
  129. omlish/logs/filters.py +9 -0
  130. omlish/logs/formatters.py +67 -0
  131. omlish/logs/utils.py +20 -0
  132. omlish/marshal/__init__.py +52 -0
  133. omlish/marshal/any.py +25 -0
  134. omlish/marshal/base.py +201 -0
  135. omlish/marshal/base64.py +25 -0
  136. omlish/marshal/dataclasses.py +115 -0
  137. omlish/marshal/datetimes.py +90 -0
  138. omlish/marshal/enums.py +43 -0
  139. omlish/marshal/exceptions.py +7 -0
  140. omlish/marshal/factories.py +129 -0
  141. omlish/marshal/global_.py +33 -0
  142. omlish/marshal/iterables.py +57 -0
  143. omlish/marshal/mappings.py +66 -0
  144. omlish/marshal/naming.py +17 -0
  145. omlish/marshal/objects.py +106 -0
  146. omlish/marshal/optionals.py +49 -0
  147. omlish/marshal/polymorphism.py +147 -0
  148. omlish/marshal/primitives.py +43 -0
  149. omlish/marshal/registries.py +57 -0
  150. omlish/marshal/standard.py +80 -0
  151. omlish/marshal/utils.py +23 -0
  152. omlish/marshal/uuids.py +29 -0
  153. omlish/marshal/values.py +30 -0
  154. omlish/math.py +184 -0
  155. omlish/os.py +32 -0
  156. omlish/reflect.py +359 -0
  157. omlish/replserver/__init__.py +5 -0
  158. omlish/replserver/__main__.py +4 -0
  159. omlish/replserver/console.py +247 -0
  160. omlish/replserver/server.py +146 -0
  161. omlish/runmodule.py +28 -0
  162. omlish/stats.py +342 -0
  163. omlish/term.py +222 -0
  164. omlish/testing/__init__.py +7 -0
  165. omlish/testing/pydevd.py +225 -0
  166. omlish/testing/pytest/__init__.py +8 -0
  167. omlish/testing/pytest/helpers.py +35 -0
  168. omlish/testing/pytest/inject/__init__.py +1 -0
  169. omlish/testing/pytest/inject/harness.py +159 -0
  170. omlish/testing/pytest/plugins/__init__.py +20 -0
  171. omlish/testing/pytest/plugins/_registry.py +6 -0
  172. omlish/testing/pytest/plugins/logging.py +13 -0
  173. omlish/testing/pytest/plugins/pycharm.py +54 -0
  174. omlish/testing/pytest/plugins/repeat.py +19 -0
  175. omlish/testing/pytest/plugins/skips.py +32 -0
  176. omlish/testing/pytest/plugins/spacing.py +19 -0
  177. omlish/testing/pytest/plugins/switches.py +70 -0
  178. omlish/testing/testing.py +102 -0
  179. omlish/text/__init__.py +0 -0
  180. omlish/text/delimit.py +171 -0
  181. omlish/text/indent.py +50 -0
  182. omlish/text/parts.py +265 -0
  183. omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
  184. omlish-0.0.0.dev1.dist-info/METADATA +17 -0
  185. omlish-0.0.0.dev1.dist-info/RECORD +187 -0
  186. omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
  187. omlish-0.0.0.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,147 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from .. import check
5
+ from .. import lang
6
+
7
+
8
+ K = ta.TypeVar('K')
9
+ V = ta.TypeVar('V')
10
+ StrMap = ta.Mapping[str, ta.Any]
11
+
12
+
13
+ class _MISSING(lang.Marker):
14
+ pass
15
+
16
+
17
+ class Flattening:
18
+
19
+ DEFAULT_DELIMITER = '.'
20
+ DEFAULT_INDEX_OPEN = '('
21
+ DEFAULT_INDEX_CLOSE = ')'
22
+
23
+ def __init__(
24
+ self,
25
+ *,
26
+ delimiter=DEFAULT_DELIMITER,
27
+ index_open=DEFAULT_INDEX_OPEN,
28
+ index_close=DEFAULT_INDEX_CLOSE,
29
+ ) -> None:
30
+ super().__init__()
31
+
32
+ self._delimiter = check.not_empty(delimiter)
33
+ self._index_open = check.not_empty(index_open)
34
+ self._index_close = check.not_empty(index_close)
35
+
36
+ def flatten(self, unflattened: StrMap) -> StrMap:
37
+ def rec(prefix: ta.List[str], value: ta.Any) -> None:
38
+ if isinstance(value, dict):
39
+ for k, v in value.items():
40
+ rec(prefix + [k], v)
41
+ elif isinstance(value, list):
42
+ check.not_empty(prefix)
43
+ for i, v in enumerate(value):
44
+ rec(prefix[:-1] + [f'{prefix[-1]}{self._index_open}{i}{self._index_close}'], v)
45
+ else:
46
+ k = self._delimiter.join(prefix)
47
+ if k in ret:
48
+ raise KeyError(k)
49
+ ret[k] = value
50
+
51
+ ret: ta.Dict[str, ta.Any] = {}
52
+ rec([], unflattened)
53
+ return ret
54
+
55
+ class UnflattenNode(lang.Abstract, ta.Generic[K]):
56
+
57
+ @abc.abstractmethod
58
+ def get(self, key: K) -> ta.Any:
59
+ raise NotImplementedError
60
+
61
+ @abc.abstractmethod
62
+ def put(self, key: K, value: ta.Any) -> None:
63
+ raise NotImplementedError
64
+
65
+ def setdefault(self, key: K, supplier: ta.Callable[[], V]) -> V:
66
+ ret = self.get(key)
67
+ if ret is _MISSING:
68
+ ret = supplier()
69
+ self.put(key, ret)
70
+ return ret
71
+
72
+ @abc.abstractmethod
73
+ def build(self) -> ta.Any:
74
+ raise NotImplementedError
75
+
76
+ @staticmethod
77
+ def maybe_build(value: ta.Any) -> ta.Any:
78
+ check.not_none(value)
79
+ return value.build() if isinstance(value, Flattening.UnflattenNode) else value
80
+
81
+ class UnflattenDict(UnflattenNode[str]):
82
+
83
+ def __init__(self) -> None:
84
+ super().__init__()
85
+
86
+ self._dict: ta.Dict[str, ta.Any] = {}
87
+
88
+ def get(self, key: str) -> ta.Any:
89
+ return self._dict.get(key, _MISSING)
90
+
91
+ def put(self, key: str, value: ta.Any) -> None:
92
+ check.arg(key not in self._dict)
93
+ self._dict[key] = value
94
+
95
+ def build(self) -> ta.Any:
96
+ return {k: Flattening.UnflattenNode.maybe_build(v) for k, v in self._dict.items()}
97
+
98
+ class UnflattenList(UnflattenNode[int]):
99
+
100
+ def __init__(self) -> None:
101
+ super().__init__()
102
+
103
+ self._list: ta.List[ta.Any] = []
104
+
105
+ def get(self, key: int) -> ta.Any:
106
+ check.arg(key >= 0)
107
+ return self._list[key] if key < len(self._list) else _MISSING
108
+
109
+ def put(self, key: int, value: ta.Any) -> None:
110
+ check.arg(key >= 0)
111
+ if key >= len(self._list):
112
+ self._list.extend([_MISSING] * (key - len(self._list) + 1))
113
+ check.arg(self._list[key] is _MISSING)
114
+ self._list[key] = value
115
+
116
+ def build(self) -> ta.Any:
117
+ return [Flattening.UnflattenNode.maybe_build(e) for e in self._list]
118
+
119
+ def unflatten(self, flattened: StrMap) -> StrMap:
120
+ root = Flattening.UnflattenDict()
121
+
122
+ def split_keys(fkey: str) -> ta.Iterable[ta.Union[str, int]]:
123
+ for part in fkey.split(self._delimiter):
124
+ if self._index_open in part:
125
+ check.state(part.endswith(self._index_close))
126
+ pos = part.index(self._index_open)
127
+ yield part[:pos]
128
+ for p in part[pos + len(self._index_open):-len(self._index_close)] \
129
+ .split(self._index_close + self._index_open):
130
+ yield int(p)
131
+ else:
132
+ check.state(')' not in part)
133
+ yield part
134
+
135
+ for fk, v in flattened.items():
136
+ node: Flattening.UnflattenNode = root
137
+ fks = list(split_keys(fk))
138
+ for key, nkey in zip(fks, fks[1:]):
139
+ if isinstance(nkey, str):
140
+ node = node.setdefault(key, Flattening.UnflattenDict)
141
+ elif isinstance(nkey, int):
142
+ node = node.setdefault(key, Flattening.UnflattenList)
143
+ else:
144
+ raise TypeError(key)
145
+ node.put(fks[-1], v)
146
+
147
+ return root.build()
@@ -0,0 +1,64 @@
1
+ """
2
+ TODO:
3
+ - \\uXXXX
4
+ """
5
+ import re
6
+ import typing as ta
7
+
8
+
9
+ _NORMALIZE_PATTERN = re.compile(r'\\([:=\s])')
10
+ _ESCAPE_PATTERN = re.compile(r'([=:\s])')
11
+ _SEPARATOR_PATTERN = re.compile(r'(?<!\\)[=:]')
12
+
13
+
14
+ def normalize(atom: str) -> str:
15
+ return _NORMALIZE_PATTERN.sub(r'\1', atom.strip())
16
+
17
+
18
+ def escape(token: str) -> str:
19
+ return _ESCAPE_PATTERN.sub(r'\\\1', token)
20
+
21
+
22
+ def parse_line(line: str) -> ta.Optional[ta.Tuple[str, str]]:
23
+ if line and not (line.startswith('#') or line.startswith('!')):
24
+ match = _SEPARATOR_PATTERN.search(line)
25
+ if match:
26
+ return normalize(line[:match.start()]), normalize(line[match.end():])
27
+ else:
28
+ space_sep = line.find(' ')
29
+ if space_sep == -1:
30
+ return normalize(line), ''
31
+ else:
32
+ return normalize(line[:space_sep]), normalize(line[space_sep:])
33
+ return None
34
+
35
+
36
+ def coalesce_lines(lines: ta.Iterable[str]) -> ta.Generator[str, None, None]:
37
+ line_iter = iter(lines)
38
+ try:
39
+ buffer = ''
40
+ while True:
41
+ line = next(line_iter)
42
+ if line.strip().endswith('\\'):
43
+ buffer += line.strip()[:-1]
44
+ else:
45
+ if buffer:
46
+ buffer += line.rstrip()
47
+ else:
48
+ buffer = line.strip()
49
+ try:
50
+ yield buffer
51
+ finally:
52
+ buffer = ''
53
+ except StopIteration:
54
+ pass
55
+
56
+
57
+ def parse_lines(lines: ta.Iterable[str]) -> ta.Dict[str, str]:
58
+ props = {}
59
+ for line in coalesce_lines(lines):
60
+ kv_pair = parse_line(line)
61
+ if kv_pair:
62
+ key, value = kv_pair
63
+ props[key] = value
64
+ return props
@@ -0,0 +1,83 @@
1
+ from dataclasses import ( # noqa
2
+ FrozenInstanceError,
3
+
4
+ MISSING,
5
+ KW_ONLY,
6
+
7
+ InitVar,
8
+ Field,
9
+
10
+ field,
11
+
12
+ dataclass,
13
+ make_dataclass,
14
+
15
+ fields,
16
+
17
+ is_dataclass,
18
+
19
+ # asdict,
20
+ # astuple,
21
+
22
+ # replace,
23
+ )
24
+
25
+ from .impl.api import ( # noqa
26
+ field as xfield,
27
+
28
+ dataclass as xdataclass,
29
+ make_dataclass as xmake_dataclass,
30
+
31
+ extra_params,
32
+ )
33
+
34
+ from .impl.as_ import ( # noqa
35
+ asdict,
36
+ astuple,
37
+ )
38
+
39
+ from .impl.replace import ( # noqa
40
+ replace,
41
+ )
42
+
43
+
44
+ ##
45
+
46
+
47
+ globals()['field'] = xfield
48
+
49
+ globals()['dataclass'] = xdataclass
50
+ globals()['make_dataclass'] = xmake_dataclass
51
+
52
+
53
+ ##
54
+
55
+
56
+ from .impl.exceptions import ( # noqa
57
+ CheckException,
58
+ )
59
+
60
+ from .impl.metaclass import ( # noqa
61
+ DataMeta,
62
+ Data,
63
+ Frozen,
64
+ Box,
65
+ )
66
+
67
+ from .impl.metadata import ( # noqa
68
+ get_merged_metadata,
69
+
70
+ UserMetadata,
71
+ metadata,
72
+
73
+ Check,
74
+ check,
75
+
76
+ Init,
77
+ init,
78
+ )
79
+
80
+ from .impl.reflect import ( # noqa
81
+ ClassInfo,
82
+ reflect,
83
+ )
@@ -0,0 +1,6 @@
1
+ """
2
+ TODO:
3
+ - metaclass:
4
+ - cleanup confer
5
+ - descriptors - check_type/validators don't handle setters lol
6
+ """
@@ -0,0 +1,260 @@
1
+ import collections.abc
2
+ import dataclasses as dc
3
+ import keyword
4
+ import sys
5
+ import types
6
+ import typing as ta
7
+
8
+ from ... import check as check_
9
+ from .internals import PARAMS_ATTR
10
+ from .internals import Params
11
+ from .main import process_class
12
+ from .metadata import METADATA_ATTR
13
+ from .metadata import Metadata
14
+ from .params import FieldExtras
15
+ from .params import Params12
16
+ from .params import ParamsExtras
17
+
18
+
19
+ MISSING = dc.MISSING
20
+
21
+ IS_12 = sys.version_info[1] >= 12
22
+
23
+
24
+ def field(
25
+ default=MISSING,
26
+ *,
27
+ default_factory=MISSING,
28
+ init=True,
29
+ repr=True,
30
+ hash=None,
31
+ compare=True,
32
+ metadata=None,
33
+ kw_only=MISSING,
34
+
35
+ coerce: ta.Optional[ta.Callable[[ta.Any], ta.Any]] = None,
36
+ check: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
37
+ check_type: ta.Optional[bool] = None,
38
+ override: bool = False,
39
+ ): # -> dc.Field
40
+ if default is not MISSING and default_factory is not MISSING:
41
+ raise ValueError('cannot specify both default and default_factory')
42
+
43
+ fx = FieldExtras(
44
+ coerce=coerce,
45
+ check=check,
46
+ check_type=check_type,
47
+ override=override,
48
+ )
49
+
50
+ md: ta.Mapping = {FieldExtras: fx}
51
+ if metadata is not None:
52
+ md = collections.ChainMap(md, check_.isinstance(metadata, collections.abc.Mapping)) # type: ignore
53
+
54
+ return dc.Field(
55
+ default,
56
+ default_factory, # noqa
57
+ init,
58
+ repr,
59
+ hash,
60
+ compare,
61
+ types.MappingProxyType(md),
62
+ kw_only, # noqa
63
+ )
64
+
65
+
66
+ def _strip_missing_values(d):
67
+ return {k: v for k, v in d.items() if v is not MISSING}
68
+
69
+
70
+ def dataclass(
71
+ cls=None,
72
+ /,
73
+ *,
74
+ init=True,
75
+ repr=True,
76
+ eq=True,
77
+ order=False,
78
+ unsafe_hash=False,
79
+ frozen=False,
80
+ match_args=True,
81
+ kw_only=False,
82
+ slots=False,
83
+ weakref_slot=False,
84
+
85
+ metadata=None,
86
+
87
+ reorder=MISSING,
88
+ cache_hash=MISSING,
89
+ generic_init=MISSING,
90
+ ):
91
+ def wrap(cls):
92
+ pkw = dict(
93
+ init=init,
94
+ repr=repr,
95
+ eq=eq,
96
+ order=order,
97
+ unsafe_hash=unsafe_hash,
98
+ frozen=frozen,
99
+ )
100
+ p12kw = dict(
101
+ match_args=match_args,
102
+ kw_only=kw_only,
103
+ slots=slots,
104
+ weakref_slot=weakref_slot,
105
+ )
106
+
107
+ dmd = cls.__dict__.get(METADATA_ATTR)
108
+
109
+ epk = dict(dmd.get(_ExtraParamsKwargs, ()) if dmd is not None else ())
110
+ epk.update(_strip_missing_values(dict(
111
+ reorder=reorder,
112
+ cache_hash=cache_hash,
113
+ generic_init=generic_init,
114
+ )))
115
+ pex = ParamsExtras(**epk)
116
+
117
+ mmd: dict = {
118
+ ParamsExtras: pex,
119
+ }
120
+
121
+ if IS_12:
122
+ pkw.update(p12kw)
123
+ else:
124
+ mmd[Params12] = Params12(**p12kw)
125
+
126
+ md: Metadata = mmd
127
+ cmds = []
128
+ if metadata is not None:
129
+ cmds.append(check_.isinstance(metadata, collections.abc.Mapping))
130
+ if dmd is not None:
131
+ cmds.append(dmd)
132
+ if cmds:
133
+ md = collections.ChainMap(md, *cmds) # type: ignore
134
+
135
+ setattr(cls, PARAMS_ATTR, Params(**pkw))
136
+ setattr(cls, METADATA_ATTR, types.MappingProxyType(md))
137
+
138
+ return process_class(cls)
139
+
140
+ if cls is None:
141
+ return wrap
142
+
143
+ return wrap(cls)
144
+
145
+
146
+ def make_dataclass(
147
+ cls_name,
148
+ fields,
149
+ *,
150
+ bases=(),
151
+ namespace=None,
152
+ init=True,
153
+ repr=True,
154
+ eq=True,
155
+ order=False,
156
+ unsafe_hash=False,
157
+ frozen=False,
158
+ match_args=True,
159
+ kw_only=False,
160
+ slots=False,
161
+ weakref_slot=False,
162
+ module=None,
163
+
164
+ reorder=MISSING,
165
+ cache_hash=MISSING,
166
+ generic_init=MISSING,
167
+ ):
168
+ if namespace is None:
169
+ namespace = {}
170
+
171
+ seen = set()
172
+ annotations = {}
173
+ defaults = {}
174
+ for item in fields:
175
+ if isinstance(item, str):
176
+ name = item
177
+ tp = 'typing.Any'
178
+ elif len(item) == 2:
179
+ name, tp, = item
180
+ elif len(item) == 3:
181
+ name, tp, spec = item
182
+ defaults[name] = spec
183
+ else:
184
+ raise TypeError(f'Invalid field: {item!r}')
185
+ if not isinstance(name, str) or not name.isidentifier():
186
+ raise TypeError(f'Field names must be valid identifiers: {name!r}')
187
+ if keyword.iskeyword(name):
188
+ raise TypeError(f'Field names must not be keywords: {name!r}')
189
+ if name in seen:
190
+ raise TypeError(f'Field name duplicated: {name!r}')
191
+
192
+ seen.add(name)
193
+ annotations[name] = tp
194
+
195
+ def exec_body_callback(ns):
196
+ ns.update(namespace)
197
+ ns.update(defaults)
198
+ ns['__annotations__'] = annotations
199
+
200
+ cls = types.new_class(cls_name, bases, {}, exec_body_callback)
201
+
202
+ if module is None:
203
+ try:
204
+ module = sys._getframemodulename(1) or '__main__' # type: ignore # noqa
205
+ except AttributeError:
206
+ try:
207
+ module = sys._getframe(1).f_globals.get('__name__', '__main__') # noqa
208
+ except (AttributeError, ValueError):
209
+ pass
210
+ if module is not None:
211
+ cls.__module__ = module
212
+
213
+ return dataclass(
214
+ cls,
215
+ init=init,
216
+ repr=repr,
217
+ eq=eq,
218
+ order=order,
219
+ unsafe_hash=unsafe_hash,
220
+ frozen=frozen,
221
+ match_args=match_args,
222
+ kw_only=kw_only,
223
+ slots=slots,
224
+ weakref_slot=weakref_slot,
225
+
226
+ reorder=reorder,
227
+ cache_hash=cache_hash,
228
+ generic_init=generic_init,
229
+ )
230
+
231
+
232
+ class _ExtraParamsKwargs:
233
+ pass
234
+
235
+
236
+ def extra_params(
237
+ *,
238
+ reorder=MISSING,
239
+ cache_hash=MISSING,
240
+ generic_init=MISSING,
241
+ ):
242
+ def inner(cls):
243
+ if PARAMS_ATTR in cls.__dict__:
244
+ raise TypeError(cls)
245
+ try:
246
+ md = cls.__dict__[METADATA_ATTR]
247
+ except KeyError:
248
+ md = {}
249
+ setattr(cls, METADATA_ATTR, md)
250
+ if _ExtraParamsKwargs in md:
251
+ raise TypeError(cls)
252
+
253
+ md[_ExtraParamsKwargs] = _strip_missing_values(dict(
254
+ reorder=reorder,
255
+ cache_hash=cache_hash,
256
+ generic_init=generic_init,
257
+ ))
258
+
259
+ return cls
260
+ return inner
@@ -0,0 +1,76 @@
1
+ import copy
2
+ import dataclasses as dc
3
+
4
+ from .internals import is_dataclass_instance
5
+ from .internals import ATOMIC_TYPES
6
+
7
+
8
+ def asdict(obj, *, dict_factory=dict):
9
+ if not is_dataclass_instance(obj): # noqa
10
+ raise TypeError("asdict() should be called on dataclass instances")
11
+ return _asdict_inner(obj, dict_factory)
12
+
13
+
14
+ def _asdict_inner(obj, dict_factory):
15
+ if type(obj) in ATOMIC_TYPES:
16
+ return obj
17
+
18
+ elif is_dataclass_instance(obj):
19
+ l = []
20
+ for f in dc.fields(obj):
21
+ value = _asdict_inner(getattr(obj, f.name), dict_factory)
22
+ l.append((f.name, value))
23
+ return dict_factory(l)
24
+
25
+ elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
26
+ return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj])
27
+
28
+ elif isinstance(obj, (list, tuple)):
29
+ return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
30
+
31
+ elif isinstance(obj, dict):
32
+ if hasattr(type(obj), 'default_factory'):
33
+ d = type(obj)(getattr(obj, 'default_factory'))
34
+ for k, v in obj.items():
35
+ d[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory)
36
+ return d
37
+ return type(obj)((_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) for k, v in obj.items())
38
+
39
+ else:
40
+ return copy.deepcopy(obj)
41
+
42
+
43
+ def astuple(obj, *, tuple_factory=tuple):
44
+ if not is_dataclass_instance(obj):
45
+ raise TypeError("astuple() should be called on dataclass instances")
46
+ return _astuple_inner(obj, tuple_factory)
47
+
48
+
49
+ def _astuple_inner(obj, tuple_factory):
50
+ if type(obj) in ATOMIC_TYPES:
51
+ return obj
52
+
53
+ elif is_dataclass_instance(obj):
54
+ l = []
55
+ for f in dc.fields(obj):
56
+ value = _astuple_inner(getattr(obj, f.name), tuple_factory)
57
+ l.append(value)
58
+ return tuple_factory(l)
59
+
60
+ elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
61
+ return type(obj)(*[_astuple_inner(v, tuple_factory) for v in obj])
62
+
63
+ elif isinstance(obj, (list, tuple)):
64
+ return type(obj)(_astuple_inner(v, tuple_factory) for v in obj)
65
+
66
+ elif isinstance(obj, dict):
67
+ obj_type = type(obj)
68
+ if hasattr(obj_type, 'default_factory'):
69
+ d = obj_type(getattr(obj, 'default_factory'))
70
+ for k, v in obj.items():
71
+ d[_astuple_inner(k, tuple_factory)] = _astuple_inner(v, tuple_factory)
72
+ return d
73
+ return obj_type((_astuple_inner(k, tuple_factory), _astuple_inner(v, tuple_factory)) for k, v in obj.items())
74
+
75
+ else:
76
+ return copy.deepcopy(obj)
@@ -0,0 +1,2 @@
1
+ class CheckException(Exception):
2
+ pass