omlish 0.0.0.dev5__py3-none-any.whl → 0.0.0.dev7__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 (163) hide show
  1. omlish/__about__.py +109 -5
  2. omlish/__init__.py +0 -8
  3. omlish/asyncs/__init__.py +9 -9
  4. omlish/asyncs/anyio.py +123 -19
  5. omlish/asyncs/asyncio.py +23 -0
  6. omlish/asyncs/asyncs.py +9 -6
  7. omlish/asyncs/bridge.py +316 -0
  8. omlish/asyncs/trio_asyncio.py +7 -3
  9. omlish/bootstrap.py +737 -0
  10. omlish/check.py +1 -1
  11. omlish/collections/__init__.py +5 -0
  12. omlish/collections/exceptions.py +2 -0
  13. omlish/collections/identity.py +7 -0
  14. omlish/collections/utils.py +38 -9
  15. omlish/configs/strings.py +96 -0
  16. omlish/dataclasses/__init__.py +16 -0
  17. omlish/dataclasses/impl/copy.py +30 -0
  18. omlish/dataclasses/impl/descriptors.py +95 -0
  19. omlish/dataclasses/impl/exceptions.py +6 -0
  20. omlish/dataclasses/impl/fields.py +24 -25
  21. omlish/dataclasses/impl/init.py +4 -2
  22. omlish/dataclasses/impl/main.py +2 -0
  23. omlish/dataclasses/impl/reflect.py +1 -1
  24. omlish/dataclasses/utils.py +67 -0
  25. omlish/{lang/datetimes.py → datetimes.py} +8 -4
  26. omlish/diag/__init__.py +4 -0
  27. omlish/diag/procfs.py +2 -2
  28. omlish/{testing → diag}/pydevd.py +35 -0
  29. omlish/diag/threads.py +131 -48
  30. omlish/dispatch/_dispatch2.py +65 -0
  31. omlish/dispatch/_dispatch3.py +104 -0
  32. omlish/docker.py +16 -1
  33. omlish/fnpairs.py +11 -4
  34. omlish/formats/__init__.py +0 -0
  35. omlish/{configs → formats}/dotenv.py +15 -24
  36. omlish/{json.py → formats/json.py} +2 -1
  37. omlish/formats/yaml.py +223 -0
  38. omlish/graphs/trees.py +1 -1
  39. omlish/http/asgi.py +2 -1
  40. omlish/http/collections.py +15 -0
  41. omlish/http/consts.py +22 -1
  42. omlish/http/sessions.py +10 -3
  43. omlish/inject/__init__.py +49 -17
  44. omlish/inject/binder.py +185 -5
  45. omlish/inject/bindings.py +3 -36
  46. omlish/inject/eagers.py +2 -8
  47. omlish/inject/elements.py +31 -10
  48. omlish/inject/exceptions.py +1 -1
  49. omlish/inject/impl/elements.py +37 -12
  50. omlish/inject/impl/injector.py +72 -25
  51. omlish/inject/impl/inspect.py +33 -5
  52. omlish/inject/impl/origins.py +77 -0
  53. omlish/inject/impl/{private.py → privates.py} +2 -2
  54. omlish/inject/impl/scopes.py +6 -2
  55. omlish/inject/injector.py +8 -4
  56. omlish/inject/inspect.py +18 -0
  57. omlish/inject/keys.py +8 -14
  58. omlish/inject/listeners.py +26 -0
  59. omlish/inject/managed.py +76 -10
  60. omlish/inject/multis.py +68 -18
  61. omlish/inject/origins.py +30 -0
  62. omlish/inject/overrides.py +5 -4
  63. omlish/inject/{private.py → privates.py} +6 -10
  64. omlish/inject/providers.py +12 -85
  65. omlish/inject/scopes.py +13 -6
  66. omlish/inject/types.py +3 -1
  67. omlish/inject/utils.py +18 -0
  68. omlish/iterators.py +69 -2
  69. omlish/lang/__init__.py +24 -9
  70. omlish/lang/cached.py +2 -2
  71. omlish/lang/classes/restrict.py +12 -1
  72. omlish/lang/classes/simple.py +18 -8
  73. omlish/lang/contextmanagers.py +13 -4
  74. omlish/lang/descriptors.py +132 -1
  75. omlish/lang/functions.py +8 -28
  76. omlish/lang/imports.py +67 -0
  77. omlish/lang/iterables.py +60 -1
  78. omlish/lang/maybes.py +3 -0
  79. omlish/lang/objects.py +38 -0
  80. omlish/lang/strings.py +25 -0
  81. omlish/lang/sys.py +9 -0
  82. omlish/lang/typing.py +42 -0
  83. omlish/lifecycles/__init__.py +34 -0
  84. omlish/lifecycles/abstract.py +43 -0
  85. omlish/lifecycles/base.py +51 -0
  86. omlish/lifecycles/contextmanagers.py +74 -0
  87. omlish/lifecycles/controller.py +116 -0
  88. omlish/lifecycles/manager.py +161 -0
  89. omlish/lifecycles/states.py +43 -0
  90. omlish/lifecycles/transitions.py +64 -0
  91. omlish/lite/__init__.py +1 -0
  92. omlish/lite/cached.py +18 -0
  93. omlish/lite/check.py +29 -0
  94. omlish/lite/contextmanagers.py +18 -0
  95. omlish/lite/json.py +30 -0
  96. omlish/lite/logs.py +52 -0
  97. omlish/lite/marshal.py +316 -0
  98. omlish/lite/reflect.py +49 -0
  99. omlish/lite/runtime.py +18 -0
  100. omlish/lite/secrets.py +19 -0
  101. omlish/lite/strings.py +25 -0
  102. omlish/lite/subprocesses.py +112 -0
  103. omlish/logs/configs.py +15 -2
  104. omlish/logs/formatters.py +7 -2
  105. omlish/marshal/__init__.py +32 -0
  106. omlish/marshal/any.py +5 -5
  107. omlish/marshal/base.py +27 -11
  108. omlish/marshal/base64.py +24 -9
  109. omlish/marshal/dataclasses.py +34 -28
  110. omlish/marshal/datetimes.py +74 -18
  111. omlish/marshal/enums.py +14 -8
  112. omlish/marshal/exceptions.py +11 -1
  113. omlish/marshal/factories.py +59 -74
  114. omlish/marshal/forbidden.py +35 -0
  115. omlish/marshal/global_.py +11 -4
  116. omlish/marshal/iterables.py +21 -24
  117. omlish/marshal/mappings.py +23 -26
  118. omlish/marshal/naming.py +4 -0
  119. omlish/marshal/numbers.py +51 -0
  120. omlish/marshal/objects.py +1 -0
  121. omlish/marshal/optionals.py +11 -12
  122. omlish/marshal/polymorphism.py +86 -21
  123. omlish/marshal/primitives.py +4 -5
  124. omlish/marshal/standard.py +13 -8
  125. omlish/marshal/uuids.py +4 -5
  126. omlish/matchfns.py +218 -0
  127. omlish/os.py +64 -0
  128. omlish/reflect/__init__.py +39 -0
  129. omlish/reflect/isinstance.py +38 -0
  130. omlish/reflect/ops.py +84 -0
  131. omlish/reflect/subst.py +110 -0
  132. omlish/reflect/types.py +275 -0
  133. omlish/secrets/__init__.py +23 -0
  134. omlish/secrets/crypto.py +132 -0
  135. omlish/secrets/marshal.py +70 -0
  136. omlish/secrets/openssl.py +207 -0
  137. omlish/secrets/passwords.py +120 -0
  138. omlish/secrets/secrets.py +299 -0
  139. omlish/secrets/subprocesses.py +42 -0
  140. omlish/sql/dbs.py +7 -6
  141. omlish/sql/duckdb.py +136 -0
  142. omlish/sql/exprs.py +12 -0
  143. omlish/sql/secrets.py +10 -0
  144. omlish/sql/sqlean.py +17 -0
  145. omlish/term.py +2 -2
  146. omlish/testing/pytest/__init__.py +3 -2
  147. omlish/testing/pytest/inject/harness.py +3 -3
  148. omlish/testing/pytest/marks.py +4 -7
  149. omlish/testing/pytest/plugins/__init__.py +1 -0
  150. omlish/testing/pytest/plugins/asyncs.py +136 -0
  151. omlish/testing/pytest/plugins/pydevd.py +1 -1
  152. omlish/testing/pytest/plugins/switches.py +54 -19
  153. omlish/text/glyphsplit.py +97 -0
  154. omlish-0.0.0.dev7.dist-info/METADATA +50 -0
  155. omlish-0.0.0.dev7.dist-info/RECORD +268 -0
  156. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/WHEEL +1 -1
  157. omlish/reflect.py +0 -355
  158. omlish-0.0.0.dev5.dist-info/METADATA +0 -34
  159. omlish-0.0.0.dev5.dist-info/RECORD +0 -212
  160. /omlish/{asyncs/futures.py → concurrent.py} +0 -0
  161. /omlish/{configs → formats}/props.py +0 -0
  162. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/LICENSE +0 -0
  163. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,316 @@
1
+ """
2
+ https://github.com/kubernetes/kubernetes/blob/60c4c2b2521fb454ce69dee737e3eb91a25e0535/pkg/controller/volume/persistentvolume/pv_controller.go#L60-L63
3
+
4
+ ==================================================================
5
+ PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE.
6
+ KEEP THE SPACE SHUTTLE FLYING.
7
+ ==================================================================
8
+
9
+ TODO:
10
+ - reuse greenlet if nested somehow?
11
+ """
12
+ import itertools
13
+ import sys
14
+ import types
15
+ import typing as ta
16
+ import weakref
17
+
18
+ from .. import lang
19
+ from .asyncs import sync_await
20
+
21
+
22
+ if ta.TYPE_CHECKING:
23
+ import asyncio
24
+
25
+ import greenlet
26
+
27
+ from . import anyio as aiu
28
+
29
+ else:
30
+ asyncio = lang.proxy_import('asyncio')
31
+
32
+ greenlet = lang.proxy_import('greenlet')
33
+
34
+ aiu = lang.proxy_import('.anyio', __package__)
35
+
36
+
37
+ T = ta.TypeVar('T')
38
+
39
+
40
+ ##
41
+
42
+
43
+ def trivial_s_to_a(fn):
44
+ async def inner(*args, **kwargs):
45
+ return fn(*args, **kwargs)
46
+ return inner
47
+
48
+
49
+ def trivial_a_to_s(fn):
50
+ def inner(*args, **kwargs):
51
+ return sync_await(fn, *args, **kwargs)
52
+ return inner
53
+
54
+
55
+ ##
56
+ # https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef
57
+
58
+
59
+ class BridgeAwaitRequiredError(Exception):
60
+ pass
61
+
62
+
63
+ class MissingBridgeGreenletError(Exception):
64
+ pass
65
+
66
+
67
+ class UnexpectedBridgeNestingError(Exception):
68
+ def __init__(self, *args, **kwargs):
69
+ super().__init__(*args, **kwargs)
70
+ # breakpoint()
71
+
72
+
73
+ #
74
+
75
+
76
+ _DEBUG_PRINT: ta.Callable[..., None] | None = None
77
+ # _DEBUG_PRINT = print # noqa
78
+
79
+ _TRACK_TRANSITION_OBJS = False
80
+
81
+
82
+ #
83
+
84
+
85
+ _BRIDGE_TRANSITIONS_SEQ = itertools.count()
86
+
87
+
88
+ class _BridgeTransition(ta.NamedTuple):
89
+ seq: int
90
+ a_to_s: bool
91
+
92
+ obj_cls: type
93
+ obj_id: int
94
+
95
+ obj: ta.Any
96
+
97
+
98
+ def _make_transition(seq: int, a_to_s: bool, obj: ta.Any) -> _BridgeTransition:
99
+ return _BridgeTransition(seq, a_to_s, obj.__class__, id(obj), (obj if _TRACK_TRANSITION_OBJS else None))
100
+
101
+
102
+ _BRIDGED_TASKS: ta.MutableMapping[ta.Any, list[_BridgeTransition]] = weakref.WeakKeyDictionary()
103
+
104
+ _BRIDGE_GREENLET_ATTR = f'__{__package__.replace(".", "__")}__bridge_greenlet__'
105
+
106
+
107
+ def _push_transition(a_to_s: bool, l: list[_BridgeTransition], t: _BridgeTransition) -> _BridgeTransition:
108
+ l.append(t)
109
+ if _DEBUG_PRINT:
110
+ _DEBUG_PRINT(f'_push_transition: {a_to_s=} {id(l)=} {t=}')
111
+ return t
112
+
113
+
114
+ def _pop_transition(a_to_s: bool, l: list[_BridgeTransition]) -> _BridgeTransition:
115
+ t = l.pop()
116
+ if _DEBUG_PRINT:
117
+ _DEBUG_PRINT(f'_pop_transition: {a_to_s=} {id(l)=} {t=}')
118
+ return t
119
+
120
+
121
+ def _get_transitions() -> list[_BridgeTransition]:
122
+ l: list[_BridgeTransition] = []
123
+
124
+ if (t := aiu.get_current_backend_task()) is not None:
125
+ try:
126
+ tl = _BRIDGED_TASKS[t]
127
+ except KeyError:
128
+ pass
129
+ else:
130
+ l.extend(tl)
131
+
132
+ g = greenlet.getcurrent()
133
+ try:
134
+ gl = getattr(g, _BRIDGE_GREENLET_ATTR)
135
+ except AttributeError:
136
+ pass
137
+ else:
138
+ l.extend(gl)
139
+
140
+ l.sort(key=lambda t: (t.seq, t.a_to_s))
141
+ return l
142
+
143
+
144
+ def is_in_bridge() -> bool:
145
+ if _DEBUG_PRINT:
146
+ _DEBUG_PRINT(_get_transitions())
147
+
148
+ if (t := aiu.get_current_backend_task()) is not None:
149
+ try:
150
+ tl = _BRIDGED_TASKS[t]
151
+ except KeyError:
152
+ last_t = None
153
+ else:
154
+ if tl:
155
+ last_t = tl[-1]
156
+ else:
157
+ last_t = None
158
+ else:
159
+ last_t = None
160
+
161
+ g = greenlet.getcurrent()
162
+ try:
163
+ gl = getattr(g, _BRIDGE_GREENLET_ATTR)
164
+ except AttributeError:
165
+ last_g = None
166
+ else:
167
+ if gl:
168
+ last_g = gl[-1]
169
+ else:
170
+ last_g = None
171
+
172
+ if last_t is None:
173
+ if last_g is None:
174
+ return False
175
+ o = last_g
176
+ else: # noqa
177
+ if last_g is None or last_g.seq < last_t.seq:
178
+ o = last_t
179
+ else:
180
+ o = last_g
181
+
182
+ in_a = (t is not None)
183
+
184
+ if _DEBUG_PRINT:
185
+ _DEBUG_PRINT(f'{o.a_to_s=} {in_a=}')
186
+
187
+ return in_a != o.a_to_s
188
+
189
+
190
+ def _safe_cancel_awaitable(awaitable: ta.Awaitable[ta.Any]) -> None:
191
+ # https://docs.python.org/3/reference/datamodel.html#coroutine.close
192
+ if asyncio.iscoroutine(awaitable):
193
+ awaitable.close() # noqa
194
+
195
+
196
+ def s_to_a_await(awaitable: ta.Awaitable[T]) -> T:
197
+ g = greenlet.getcurrent()
198
+
199
+ if not getattr(g, _BRIDGE_GREENLET_ATTR, False):
200
+ _safe_cancel_awaitable(awaitable)
201
+ raise MissingBridgeGreenletError
202
+
203
+ return g.parent.switch(awaitable)
204
+
205
+
206
+ def s_to_a(fn, *, require_await=False):
207
+ @types.coroutine
208
+ def outer(*args, **kwargs):
209
+ def inner():
210
+ try:
211
+ return fn(*args, **kwargs)
212
+ finally:
213
+ if (gl2 := getattr(g, _BRIDGE_GREENLET_ATTR)) is not gl: # noqa
214
+ raise UnexpectedBridgeNestingError
215
+ if (cur_g := _pop_transition(False, gl)) is not added_g: # noqa
216
+ raise UnexpectedBridgeNestingError
217
+ if gl:
218
+ raise UnexpectedBridgeNestingError
219
+
220
+ seq = next(_BRIDGE_TRANSITIONS_SEQ)
221
+
222
+ g = greenlet.greenlet(inner)
223
+ setattr(g, _BRIDGE_GREENLET_ATTR, gl := []) # type: ignore
224
+ added_g = _push_transition(False, gl, _make_transition(seq, False, g))
225
+
226
+ if (t := aiu.get_current_backend_task()) is not None:
227
+ try:
228
+ tl = _BRIDGED_TASKS[t]
229
+ except KeyError:
230
+ tl = _BRIDGED_TASKS[t] = []
231
+ added_t = _push_transition(False, tl, _make_transition(seq, False, g))
232
+
233
+ try:
234
+ result: ta.Any = g.switch()
235
+ switch_occurred = False
236
+ while not g.dead:
237
+ switch_occurred = True
238
+ try:
239
+ value = yield result
240
+ except BaseException: # noqa
241
+ result = g.throw(*sys.exc_info())
242
+ else:
243
+ result = g.switch(value)
244
+
245
+ if require_await and not switch_occurred:
246
+ raise BridgeAwaitRequiredError
247
+
248
+ return result
249
+
250
+ finally:
251
+ if t is not None:
252
+ if (tl2 := _BRIDGED_TASKS[t]) is not tl: # noqa
253
+ raise UnexpectedBridgeNestingError
254
+ if (cur_t := _pop_transition(False, tl)) is not added_t: # noqa
255
+ raise UnexpectedBridgeNestingError
256
+
257
+ return outer
258
+
259
+
260
+ def a_to_s(fn):
261
+ def inner(*args, **kwargs):
262
+ seq = next(_BRIDGE_TRANSITIONS_SEQ)
263
+
264
+ if (t := aiu.get_current_backend_task()) is not None:
265
+ try:
266
+ tl = _BRIDGED_TASKS[t]
267
+ except KeyError:
268
+ tl = _BRIDGED_TASKS[t] = []
269
+ added_t = _push_transition(True, tl, _make_transition(seq, True, t))
270
+ else:
271
+ added_t = None
272
+
273
+ g = greenlet.getcurrent()
274
+ try:
275
+ gl = getattr(g, _BRIDGE_GREENLET_ATTR)
276
+ except AttributeError:
277
+ setattr(g, _BRIDGE_GREENLET_ATTR, gl := [])
278
+ added_g = _push_transition(True, gl, _make_transition(seq, True, g))
279
+
280
+ try:
281
+ ret = missing = object()
282
+
283
+ async def gate():
284
+ nonlocal ret
285
+ ret = await fn(*args, **kwargs)
286
+
287
+ cr = gate()
288
+ sv = None
289
+ try:
290
+ while True:
291
+ try:
292
+ sv = cr.send(sv)
293
+ except StopIteration:
294
+ break
295
+
296
+ if ret is missing or cr.cr_await is not None or cr.cr_running:
297
+ sv = s_to_a_await(sv)
298
+
299
+ finally:
300
+ cr.close()
301
+
302
+ finally:
303
+ if t is not None:
304
+ if (tl2 := _BRIDGED_TASKS[t]) is not tl: # noqa
305
+ raise UnexpectedBridgeNestingError
306
+ if (cur_t := _pop_transition(True, tl)) is not added_t: # noqa
307
+ raise UnexpectedBridgeNestingError
308
+
309
+ if (gl2 := getattr(g, _BRIDGE_GREENLET_ATTR)) is not gl: # noqa
310
+ raise UnexpectedBridgeNestingError
311
+ if (cur_g := _pop_transition(True, gl)) is not added_g: # noqa
312
+ raise UnexpectedBridgeNestingError
313
+
314
+ return ret
315
+
316
+ return inner
@@ -9,6 +9,7 @@ if ta.TYPE_CHECKING:
9
9
 
10
10
  import sniffio
11
11
  import trio_asyncio
12
+
12
13
  else:
13
14
  asyncio = lang.proxy_import('asyncio')
14
15
 
@@ -21,7 +22,7 @@ def check_trio_asyncio() -> None:
21
22
  raise RuntimeError('trio_asyncio loop not running')
22
23
 
23
24
 
24
- def with_trio_asyncio_loop(*, wait=False):
25
+ def with_trio_asyncio_loop(*, wait=False, strict=False):
25
26
  def outer(fn):
26
27
  @functools.wraps(fn)
27
28
  async def inner(*args, **kwargs):
@@ -30,7 +31,10 @@ def with_trio_asyncio_loop(*, wait=False):
30
31
  return
31
32
 
32
33
  if sniffio.current_async_library() != 'trio':
33
- raise RuntimeError('trio loop not running')
34
+ if strict:
35
+ raise RuntimeError('trio loop not running')
36
+ await fn(*args, **kwargs)
37
+ return
34
38
 
35
39
  loop: asyncio.BaseEventLoop
36
40
  async with trio_asyncio.open_loop() as loop:
@@ -40,7 +44,7 @@ def with_trio_asyncio_loop(*, wait=False):
40
44
  if wait:
41
45
  # FIXME: lol
42
46
  while asyncio.all_tasks(loop):
43
- await asyncio.sleep(.2)
47
+ await asyncio.sleep(.1)
44
48
 
45
49
  return inner
46
50