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
omlish/logs/_abc.py ADDED
@@ -0,0 +1,247 @@
1
+ import types
2
+ import typing as ta
3
+
4
+
5
+ ##
6
+
7
+
8
+ Level: ta.TypeAlias = int
9
+
10
+
11
+ ExceptionInfo: ta.TypeAlias = tuple[type[BaseException], BaseException, types.TracebackType]
12
+
13
+
14
+ class LogRecord:
15
+ name: str
16
+ msg: str
17
+ args: tuple
18
+ levelname: str
19
+ levelno: Level
20
+ pathname: str
21
+ filename: str
22
+ module: str
23
+ exc_info: ExceptionInfo | None
24
+ exc_text: str | None
25
+ stack_info: str | None
26
+ lineno: int
27
+ funcName: str
28
+ created: float
29
+ msecs: float
30
+ relativeCreated: float
31
+ thread: int
32
+ threadName: str
33
+ processName: str
34
+ process: int
35
+
36
+
37
+ ##
38
+
39
+
40
+ class Formatter(ta.Protocol):
41
+ default_time_format: ta.ClassVar[str]
42
+ default_msec_format: ta.ClassVar[str]
43
+
44
+ def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: ...
45
+
46
+ def formatException(self, ei: ExceptionInfo) -> str: ...
47
+
48
+ def usesTime(self) -> bool: ...
49
+
50
+ def formatMessage(self, record: LogRecord) -> str: ...
51
+
52
+ def formatStack(self, stack_info: str) -> str: ...
53
+
54
+ def format(self, record: LogRecord) -> str: ...
55
+
56
+
57
+ class BufferingFormatter(ta.Protocol):
58
+ def formatHeader(self, records: ta.Sequence[LogRecord]) -> str: ...
59
+
60
+ def formatFooter(self, records: ta.Sequence[LogRecord]) -> str: ...
61
+
62
+ def format(self, records: ta.Sequence[LogRecord]) -> str: ...
63
+
64
+
65
+ ##
66
+
67
+
68
+ class Filter(ta.Protocol):
69
+ def filter(self, record: LogRecord) -> bool: ...
70
+
71
+
72
+ class Filterer(ta.Protocol):
73
+ def addFilter(self, filter: Filter) -> None: ...
74
+
75
+ def removeFilter(self, filter: Filter) -> None: ...
76
+
77
+ def filter(self, record: LogRecord) -> bool: ...
78
+
79
+
80
+ ##
81
+
82
+
83
+ class Handler(ta.Protocol):
84
+ level: Level
85
+
86
+ def get_name(self) -> str: ...
87
+
88
+ def set_name(self, name: str) -> None: ...
89
+
90
+ name: str
91
+
92
+ def createLock(self) -> None: ...
93
+
94
+ def acquire(self) -> None: ...
95
+
96
+ def release(self) -> None: ...
97
+
98
+ def setLevel(self, level: Level) -> None: ...
99
+
100
+ def format(self, record: LogRecord) -> str: ...
101
+
102
+ def emit(self, record: LogRecord) -> None: ...
103
+
104
+ def handle(self, record: LogRecord) -> bool: ...
105
+
106
+ def setFormatter(self, fmt: Formatter) -> None: ...
107
+
108
+ def flush(self) -> None: ...
109
+
110
+ def close(self) -> None: ...
111
+
112
+ def handleError(self, record: LogRecord) -> None: ...
113
+
114
+
115
+ class Stream(ta.Protocol):
116
+ def write(self, s: str) -> None: ...
117
+
118
+ def flush(self) -> None: ... # OPTIONAL METHOD
119
+
120
+ def close(self) -> None: ... # OPTIONAL METHOD
121
+
122
+
123
+ class StreamHandler(Handler):
124
+ terminator: ta.ClassVar[str]
125
+
126
+ stream: Stream
127
+
128
+ def flush(self) -> None: ...
129
+
130
+ def emit(self, record: LogRecord) -> None: ...
131
+
132
+ def setStream(self, stream: Stream) -> None: ...
133
+
134
+
135
+ ##
136
+
137
+
138
+ class Manager(ta.Protocol):
139
+ root: 'Logger'
140
+
141
+ disable: Level
142
+
143
+ def getLogger(self, name: str) -> 'Logger': ...
144
+
145
+ def setLoggerClass(self, klass: type['Logger']) -> None: ...
146
+
147
+ def setLogRecordFactory(self, factory: ta.Callable[..., LogRecord]) -> None: ... # UNREFERENCED?
148
+
149
+
150
+ ##
151
+
152
+
153
+ Caller: ta.TypeAlias = tuple[
154
+ str, # filename
155
+ int, # lineno
156
+ str, # name
157
+ str, # formatted
158
+ ]
159
+
160
+
161
+ class Logger(Filterer, ta.Protocol):
162
+ name: str
163
+ level: Level
164
+ parent: ta.Optional['Logger']
165
+ propagate: bool
166
+ handlers: list[Handler]
167
+ disabled: bool
168
+
169
+ manager: Manager
170
+
171
+ def setLevel(self, level: Level) -> None: ...
172
+
173
+ def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
174
+ def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
175
+ def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
176
+ def warn(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
177
+ def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
178
+ def exception(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
179
+ def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
180
+ def fatal(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
181
+
182
+ def log(self, level: Level, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
183
+
184
+ def findCaller(self, stack_info: bool = False, stacklevel: int = 1) -> Caller: ... #
185
+
186
+ def makeRecord(
187
+ self,
188
+ name,
189
+ level,
190
+ fn,
191
+ lno,
192
+ msg,
193
+ args,
194
+ exc_info,
195
+ func: str | None = None,
196
+ extra: ta.Mapping[str, ta.Any] | None = None,
197
+ sinfo: str | None = None,
198
+ ) -> LogRecord: ...
199
+
200
+ def handle(self, record: LogRecord) -> None: ...
201
+
202
+ def addHandler(self, hdlr: Handler) -> None: ...
203
+
204
+ def removeHandler(self, hdlr: Handler) -> None: ...
205
+
206
+ def hasHandlers(self) -> bool: ...
207
+
208
+ def callHandlers(self, record: LogRecord) -> None: ...
209
+
210
+ def getEffectiveLevel(self) -> Level: ...
211
+
212
+ def isEnabledFor(self, level: Level) -> bool: ...
213
+
214
+ def getChild(self, suffix: str) -> 'Logger': ...
215
+
216
+
217
+ ##
218
+
219
+
220
+ class LoggerAdapter(ta.Protocol):
221
+ logger: Logger
222
+ extra: ta.Mapping[str, ta.Any]
223
+
224
+ def process(self, msg: str, kwargs: dict[str, ta.Any]) -> tuple[str, dict[str, ta.Any]]: ...
225
+
226
+ def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
227
+ def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
228
+ def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
229
+ def warn(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
230
+ def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
231
+ def exception(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
232
+ def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
233
+ def fatal(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
234
+
235
+ def log(self, level: Level, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
236
+
237
+ def isEnabledFor(self, level: Level) -> bool: ...
238
+
239
+ def setLevel(self, level: Level) -> None: ...
240
+
241
+ def getEffectiveLevel(self) -> Level: ...
242
+
243
+ def hasHandlers(self) -> bool: ...
244
+
245
+ manager: Manager
246
+
247
+ name: str
omlish/logs/configs.py ADDED
@@ -0,0 +1,62 @@
1
+ import dataclasses as dc
2
+ import logging
3
+ import typing as ta
4
+
5
+ from .filters import TidFilter
6
+ from .formatters import StandardLogFormatter
7
+
8
+
9
+ NOISY_LOGGERS: set[str] = {
10
+ 'boto3.resources.action',
11
+ 'datadog.dogstatsd',
12
+ 'elasticsearch',
13
+ 'kazoo.client',
14
+ 'requests.packages.urllib3.connectionpool',
15
+ }
16
+
17
+
18
+ @dc.dataclass()
19
+ class DictConfig:
20
+ version: int = 1
21
+ incremental: bool = False
22
+ disable_existing_loggers: bool = False
23
+ filters: dict[str, 'FilterConfig'] = dc.field(default_factory=dict)
24
+ formatters: dict[str, 'FormatterConfig'] = dc.field(default_factory=dict)
25
+ handlers: dict[str, 'HandlerConfig'] = dc.field(default_factory=dict)
26
+ loggers: dict[str, 'LoggerConfig'] = dc.field(default_factory=dict)
27
+ root: ta.Optional['LoggerConfig'] = None
28
+
29
+
30
+ FilterConfig = dict[str, ta.Any]
31
+ FormatterConfig = dict[str, ta.Any]
32
+ HandlerConfig = dict[str, ta.Any]
33
+ LoggerConfig = dict[str, ta.Any]
34
+
35
+ STANDARD_LOG_FORMAT_PARTS = [
36
+ ('asctime', '%(asctime)-15s'),
37
+ ('process', 'pid=%(process)-6s'),
38
+ ('thread', 'tid=%(thread)-16s'),
39
+ ('levelname', '%(levelname)-8s'),
40
+ ('name', '%(name)s'),
41
+ ('separator', '::'),
42
+ ('message', '%(message)s'),
43
+ ]
44
+
45
+
46
+ def build_log_format(parts: ta.Iterable[tuple[str, str]]) -> str:
47
+ return ' '.join(v for k, v in parts)
48
+
49
+
50
+ def configure_standard_logging(level: ta.Any = None) -> logging.Handler:
51
+ handler = logging.StreamHandler()
52
+ handler.setFormatter(StandardLogFormatter(build_log_format(STANDARD_LOG_FORMAT_PARTS)))
53
+ handler.addFilter(TidFilter())
54
+ logging.root.addHandler(handler)
55
+
56
+ if level is not None:
57
+ logging.root.setLevel(level)
58
+
59
+ for noisy_logger in NOISY_LOGGERS:
60
+ logging.getLogger(noisy_logger).setLevel(logging.WARNING)
61
+
62
+ return handler
omlish/logs/filters.py ADDED
@@ -0,0 +1,9 @@
1
+ import logging
2
+ import threading
3
+
4
+
5
+ class TidFilter(logging.Filter):
6
+
7
+ def filter(self, record):
8
+ record.tid = threading.get_native_id()
9
+ return True
@@ -0,0 +1,67 @@
1
+ import datetime
2
+ import logging
3
+
4
+ from .. import json
5
+ from .. import term
6
+
7
+
8
+ class StandardLogFormatter(logging.Formatter):
9
+
10
+ converter = datetime.datetime.fromtimestamp # type: ignore
11
+
12
+ def formatTime(self, record, datefmt=None):
13
+ ct = self.converter(record.created) # type: ignore
14
+ if datefmt:
15
+ return ct.strftime(datefmt) # noqa
16
+ else:
17
+ t = ct.strftime("%Y-%m-%d %H:%M:%S") # noqa
18
+ return '%s.%03d' % (t, record.msecs)
19
+
20
+
21
+ class ColorLogFormatter(StandardLogFormatter):
22
+
23
+ LEVEL_COLORS = {
24
+ logging.WARNING: term.SGRs.FG.BRIGHT_YELLOW,
25
+ logging.ERROR: term.SGRs.FG.BRIGHT_RED,
26
+ logging.CRITICAL: term.SGRs.FG.BRIGHT_RED,
27
+ }
28
+
29
+ def formatMessage(self, record):
30
+ buf = super().formatMessage(record)
31
+ try:
32
+ c = self.LEVEL_COLORS[record.levelno]
33
+ except KeyError:
34
+ pass
35
+ else:
36
+ buf = term.SGR(c) + buf + term.SGR(term.SGRs.RESET)
37
+ return buf
38
+
39
+
40
+ class JsonLogFormatter(logging.Formatter):
41
+
42
+ KEYS = {
43
+ 'name': False,
44
+ 'msg': False,
45
+ 'args': False,
46
+ 'levelname': False,
47
+ 'levelno': False,
48
+ 'pathname': False,
49
+ 'filename': False,
50
+ 'module': False,
51
+ 'exc_info': True,
52
+ 'exc_text': True,
53
+ 'stack_info': True,
54
+ 'lineno': False,
55
+ 'funcName': False,
56
+ 'created': False,
57
+ 'msecs': False,
58
+ 'relativeCreated': False,
59
+ 'thread': False,
60
+ 'threadName': False,
61
+ 'processName': False,
62
+ 'process': False,
63
+ }
64
+
65
+ def format(self, record: logging.LogRecord) -> str:
66
+ dct = {k: v for k, o in self.KEYS.items() for v in [getattr(record, k)] if not (o and v is None)}
67
+ return json.dumps_compact(dct)
omlish/logs/utils.py ADDED
@@ -0,0 +1,20 @@
1
+ import functools
2
+ import logging
3
+
4
+
5
+ log = logging.getLogger(__name__)
6
+
7
+
8
+ def error_logging(log=log):
9
+ def outer(fn):
10
+ @functools.wraps(fn)
11
+ def inner(*args, **kwargs):
12
+ try:
13
+ return fn(*args, **kwargs)
14
+ except Exception:
15
+ log.exception(f'Error in {fn!r}')
16
+ raise
17
+
18
+ return inner
19
+
20
+ return outer
@@ -0,0 +1,52 @@
1
+ from .base import ( # noqa
2
+ Marshaler,
3
+ Unmarshaler,
4
+
5
+ MarshalerFactory,
6
+ UnmarshalerFactory,
7
+
8
+ FuncMarshaler,
9
+ FuncUnmarshaler,
10
+
11
+ BaseContext,
12
+ MarshalContext,
13
+ UnmarshalContext,
14
+
15
+ RecursiveMarshalerFactory,
16
+ RecursiveUnmarshalerFactory,
17
+
18
+ SetType,
19
+ )
20
+
21
+ from .naming import ( # noqa
22
+ Naming,
23
+ translate_name,
24
+ )
25
+
26
+ from .global_ import ( # noqa
27
+ marshal,
28
+ unmarshal,
29
+ )
30
+
31
+ from .objects import ( # noqa
32
+ FieldMetadata,
33
+ ObjectMetadata,
34
+ )
35
+
36
+ from .polymorphism import ( # noqa
37
+ Impl,
38
+ Polymorphism,
39
+ polymorphism_from_subclasses,
40
+ )
41
+
42
+ from .registries import ( # noqa
43
+ Registry,
44
+ )
45
+
46
+ from .standard import ( # noqa
47
+ STANDARD_MARSHALER_FACTORIES,
48
+ new_standard_marshaler_factory,
49
+
50
+ STANDARD_UNMARSHALER_FACTORIES,
51
+ new_standard_unmarshaler_factory,
52
+ )
omlish/marshal/any.py ADDED
@@ -0,0 +1,25 @@
1
+ import typing as ta
2
+
3
+ from .base import MarshalContext
4
+ from .base import Marshaler
5
+ from .base import MarshalerFactory
6
+ from .base import UnmarshalContext
7
+ from .base import Unmarshaler
8
+ from .base import UnmarshalerFactory
9
+ from .factories import TypeMapFactory
10
+ from .values import Value
11
+
12
+
13
+ class AnyMarshalerUnmarshaler(Marshaler, Unmarshaler):
14
+
15
+ def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
16
+ return ctx.make(type(o)).marshal(ctx, o)
17
+
18
+ def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
19
+ return v
20
+
21
+
22
+ ANY_MARSHALER_UNMARSHALER = AnyMarshalerUnmarshaler()
23
+
24
+ ANY_MARSHALER_FACTORY: MarshalerFactory = TypeMapFactory({ta.Any: ANY_MARSHALER_UNMARSHALER}) # type: ignore
25
+ ANY_UNMARSHALER_FACTORY: UnmarshalerFactory = TypeMapFactory({ta.Any: ANY_MARSHALER_UNMARSHALER}) # type: ignore
omlish/marshal/base.py ADDED
@@ -0,0 +1,201 @@
1
+ """
2
+ TODO:
3
+ - mappings
4
+ - redacted
5
+ - strongly typed Composite/Cached Marshaler/Unmarshaler factories - footgun
6
+ - streaming? Start/EndObject, etc..
7
+
8
+ https://github.com/python-attrs/cattrs:
9
+ *
10
+
11
+ Jackson:
12
+ - USE_ANNOTATIONS
13
+ - AUTO_DETECT_CREATORS
14
+ - AUTO_DETECT_FIELDS
15
+ - AUTO_DETECT_GETTERS
16
+ - AUTO_DETECT_IS_GETTERS
17
+ - AUTO_DETECT_SETTERS
18
+ - REQUIRE_SETTERS_FOR_GETTERS
19
+ - USE_GETTERS_AS_SETTERS
20
+ - INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES
21
+ - INFER_PROPERTY_MUTATORS
22
+ - ALLOW_FINAL_FIELDS_AS_MUTATORS
23
+ - ALLOW_VOID_VALUED_PROPERTIES
24
+ - CAN_OVERRIDE_ACCESS_MODIFIERS
25
+ - OVERRIDE_PUBLIC_ACCESS_MODIFIERS
26
+ - SORT_PROPERTIES_ALPHABETICALLY
27
+ - USE_WRAPPER_NAME_AS_PROPERTY_NAME
28
+ - ACCEPT_CASE_INSENSITIVE_ENUMS
29
+ - ACCEPT_CASE_INSENSITIVE_PROPERTIES
30
+ - ACCEPT_CASE_INSENSITIVE_VALUES
31
+ - ALLOW_EXPLICIT_PROPERTY_RENAMING
32
+ - USE_STD_BEAN_NAMING
33
+ - ALLOW_COERCION_OF_SCALARS
34
+ - DEFAULT_VIEW_INCLUSION
35
+ - IGNORE_DUPLICATE_MODULE_REGISTRATIONS
36
+ - IGNORE_MERGE_FOR_UNMERGEABLE
37
+ - USE_BASE_TYPE_AS_DEFAULT_IMPL
38
+ - USE_STATIC_TYPING
39
+ - BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES
40
+
41
+ https://github.com/yukinarit/pyserde
42
+ - datatypes
43
+ - typing.Union
44
+ - typing.NewType for primitive types
45
+ - typing.Any
46
+ - typing.Literal
47
+ - typing.Generic
48
+ - typing.ClassVar
49
+ - dataclasses.InitVar
50
+ - Enum and IntEnum
51
+ - pathlib.Path
52
+ - decimal.Decimal
53
+ - uuid.UUID
54
+ - datetime.date, datetime.time, datetime.datetime
55
+ - ipaddress
56
+ - numpy types
57
+ - Class Attributes
58
+ - Field Attributes
59
+ - Decorators
60
+ - Type Check
61
+ - Union Representation
62
+ - Forward reference
63
+ - PEP563 Postponed Evaluation of Annotations
64
+ - PEP585 Type Hinting Generics In Standard Collections
65
+ - PEP604 Allow writing union types as X | Y
66
+ - PEP681 Data Class Transform
67
+ - Case Conversion
68
+ - Rename
69
+ - Alias
70
+ - Skip (de)serialization (skip, skip_if, skip_if_false, skip_if_default)
71
+ - Custom field (de)serializer
72
+ - Custom class (de)serializer
73
+ - Custom global (de)serializer
74
+ - Flatten
75
+ """
76
+ import abc
77
+ import dataclasses as dc
78
+ import typing as ta
79
+
80
+ from .. import check
81
+ from .. import collections as col
82
+ from .. import lang
83
+ from .. import reflect as rfl
84
+ from .exceptions import UnhandledTypeException
85
+ from .factories import Factory
86
+ from .factories import RecursiveTypeFactory
87
+ from .registries import Registry
88
+ from .registries import RegistryItem
89
+ from .utils import _Proxy
90
+ from .values import Value
91
+
92
+
93
+ ##
94
+
95
+
96
+ class Marshaler(lang.Abstract):
97
+ @abc.abstractmethod
98
+ def marshal(self, ctx: 'MarshalContext', o: ta.Any) -> Value:
99
+ raise NotImplementedError
100
+
101
+
102
+ class Unmarshaler(lang.Abstract):
103
+ @abc.abstractmethod
104
+ def unmarshal(self, ctx: 'UnmarshalContext', v: Value) -> ta.Any:
105
+ raise NotImplementedError
106
+
107
+
108
+ MarshalerFactory = Factory[Marshaler, 'MarshalContext', rfl.Type]
109
+ UnmarshalerFactory = Factory[Unmarshaler, 'UnmarshalContext', rfl.Type]
110
+
111
+
112
+ ##
113
+
114
+
115
+ @dc.dataclass(frozen=True)
116
+ class FuncMarshaler(Marshaler, lang.Final):
117
+ fn: ta.Callable[['MarshalContext', ta.Any], Value]
118
+
119
+ def marshal(self, ctx: 'MarshalContext', o: ta.Any) -> Value:
120
+ return self.fn(ctx, o)
121
+
122
+
123
+ @dc.dataclass(frozen=True)
124
+ class FuncUnmarshaler(Unmarshaler, lang.Final):
125
+ fn: ta.Callable[['UnmarshalContext', Value], ta.Any]
126
+
127
+ def unmarshal(self, ctx: 'UnmarshalContext', v: Value) -> ta.Any:
128
+ return self.fn(ctx, v)
129
+
130
+
131
+ ##
132
+
133
+
134
+ class Option:
135
+ pass
136
+
137
+
138
+ ##
139
+
140
+
141
+ @dc.dataclass(frozen=True)
142
+ class BaseContext(lang.Abstract):
143
+ registry: Registry
144
+ options: col.TypeMap[Option] = col.TypeMap()
145
+
146
+
147
+ @dc.dataclass(frozen=True)
148
+ class MarshalContext(BaseContext, lang.Final):
149
+ factory: ta.Optional[MarshalerFactory] = None
150
+
151
+ def make(self, o: ta.Any) -> Marshaler:
152
+ rty = rfl.type_(o)
153
+ if (m := check.not_none(self.factory)(self, rty)) is not None: # noqa
154
+ return m
155
+ raise UnhandledTypeException(rty)
156
+
157
+
158
+ @dc.dataclass(frozen=True)
159
+ class UnmarshalContext(BaseContext, lang.Final):
160
+ factory: ta.Optional[UnmarshalerFactory] = None
161
+
162
+ def make(self, o: ta.Any) -> Unmarshaler:
163
+ rty = rfl.type_(o)
164
+ if (m := check.not_none(self.factory)(self, rty)) is not None: # noqa
165
+ return m
166
+ raise UnhandledTypeException(rty)
167
+
168
+
169
+ ##
170
+
171
+
172
+ class _ProxyMarshaler(_Proxy[Marshaler], Marshaler):
173
+ def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
174
+ return self._obj.marshal(ctx, o)
175
+
176
+
177
+ class RecursiveMarshalerFactory(RecursiveTypeFactory[Marshaler, MarshalContext], lang.Final):
178
+ def __init__(self, f: MarshalerFactory) -> None:
179
+ super().__init__(f, _ProxyMarshaler._new) # noqa
180
+
181
+
182
+ class _ProxyUnmarshaler(_Proxy[Unmarshaler], Unmarshaler):
183
+ def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
184
+ return self._obj.unmarshal(ctx, v)
185
+
186
+
187
+ class RecursiveUnmarshalerFactory(RecursiveTypeFactory[Unmarshaler, UnmarshalContext], lang.Final):
188
+ def __init__(self, f: UnmarshalerFactory) -> None:
189
+ super().__init__(f, _ProxyUnmarshaler._new) # noqa
190
+
191
+
192
+ ##
193
+
194
+
195
+ @dc.dataclass(frozen=True)
196
+ class SetType(RegistryItem, lang.Final):
197
+ marshaler: ta.Optional[Marshaler] = None
198
+ marshaler_factory: ta.Optional[MarshalerFactory] = None
199
+
200
+ unmarshaler: ta.Optional[Unmarshaler] = None
201
+ unmarshaler_factory: ta.Optional[UnmarshalerFactory] = None
@@ -0,0 +1,25 @@
1
+ import base64
2
+
3
+ from .. import check
4
+ from .base import MarshalContext
5
+ from .base import Marshaler
6
+ from .base import MarshalerFactory
7
+ from .base import UnmarshalContext
8
+ from .base import Unmarshaler
9
+ from .base import UnmarshalerFactory
10
+ from .factories import TypeMapFactory
11
+ from .values import Value
12
+
13
+
14
+ class Base64MarshalerUnmarshaler(Marshaler, Unmarshaler):
15
+ def marshal(self, ctx: MarshalContext, o: bytes) -> str:
16
+ return base64.b64encode(o).decode()
17
+
18
+ def unmarshal(self, ctx: UnmarshalContext, v: Value) -> bytes:
19
+ return base64.b64decode(check.isinstance(v, str).encode())
20
+
21
+
22
+ BASE64_MARSHALER_UNMARSHALER = Base64MarshalerUnmarshaler()
23
+
24
+ BASE64_MARSHALER_FACTORY: MarshalerFactory = TypeMapFactory({bytes: BASE64_MARSHALER_UNMARSHALER})
25
+ BASE64_UNMARSHALER_FACTORY: UnmarshalerFactory = TypeMapFactory({bytes: BASE64_MARSHALER_UNMARSHALER})