omextra 0.0.0.dev432__tar.gz → 0.0.0.dev434__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 (76) hide show
  1. {omextra-0.0.0.dev432/omextra.egg-info → omextra-0.0.0.dev434}/PKG-INFO +2 -2
  2. omextra-0.0.0.dev434/omextra/asyncs/bridge.py +359 -0
  3. omextra-0.0.0.dev434/omextra/text/antlr/cli/__init__.py +0 -0
  4. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434/omextra.egg-info}/PKG-INFO +2 -2
  5. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra.egg-info/SOURCES.txt +2 -0
  6. omextra-0.0.0.dev434/omextra.egg-info/requires.txt +1 -0
  7. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/pyproject.toml +2 -2
  8. omextra-0.0.0.dev432/omextra.egg-info/requires.txt +0 -1
  9. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/LICENSE +0 -0
  10. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/MANIFEST.in +0 -0
  11. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/README.md +0 -0
  12. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/.omlish-manifests.json +0 -0
  13. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/__about__.py +0 -0
  14. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/__init__.py +0 -0
  15. {omextra-0.0.0.dev432/omextra/formats → omextra-0.0.0.dev434/omextra/asyncs}/__init__.py +0 -0
  16. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/defs.py +0 -0
  17. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/dynamic.py +0 -0
  18. {omextra-0.0.0.dev432/omextra/formats/json → omextra-0.0.0.dev434/omextra/formats}/__init__.py +0 -0
  19. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/formats/json/Json.g4 +0 -0
  20. {omextra-0.0.0.dev432/omextra/formats/json/_antlr → omextra-0.0.0.dev434/omextra/formats/json}/__init__.py +0 -0
  21. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/formats/json/_antlr/JsonLexer.py +0 -0
  22. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/formats/json/_antlr/JsonListener.py +0 -0
  23. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/formats/json/_antlr/JsonParser.py +0 -0
  24. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/formats/json/_antlr/JsonVisitor.py +0 -0
  25. {omextra-0.0.0.dev432/omextra/io → omextra-0.0.0.dev434/omextra/formats/json/_antlr}/__init__.py +0 -0
  26. {omextra-0.0.0.dev432/omextra/specs → omextra-0.0.0.dev434/omextra/io}/__init__.py +0 -0
  27. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/io/trampoline.py +0 -0
  28. {omextra-0.0.0.dev432/omextra/specs/irc → omextra-0.0.0.dev434/omextra/specs}/__init__.py +0 -0
  29. {omextra-0.0.0.dev432/omextra/specs/irc/messages → omextra-0.0.0.dev434/omextra/specs/irc}/__init__.py +0 -0
  30. {omextra-0.0.0.dev432/omextra/specs/irc/numerics → omextra-0.0.0.dev434/omextra/specs/irc/messages}/__init__.py +0 -0
  31. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/messages/base.py +0 -0
  32. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/messages/formats.py +0 -0
  33. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/messages/messages.py +0 -0
  34. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/messages/parsing.py +0 -0
  35. {omextra-0.0.0.dev432/omextra/specs/proto → omextra-0.0.0.dev434/omextra/specs/irc/numerics}/__init__.py +0 -0
  36. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/numerics/formats.py +0 -0
  37. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/numerics/numerics.py +0 -0
  38. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/numerics/types.py +0 -0
  39. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/LICENSE +0 -0
  40. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/__init__.py +0 -0
  41. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/consts.py +0 -0
  42. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/errors.py +0 -0
  43. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/message.py +0 -0
  44. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/nuh.py +0 -0
  45. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/parsing.py +0 -0
  46. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/rendering.py +0 -0
  47. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/tags.py +0 -0
  48. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/irc/protocol/utils.py +0 -0
  49. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/Protobuf3.g4 +0 -0
  50. {omextra-0.0.0.dev432/omextra/specs/proto/_antlr → omextra-0.0.0.dev434/omextra/specs/proto}/__init__.py +0 -0
  51. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/_antlr/Protobuf3Lexer.py +0 -0
  52. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/_antlr/Protobuf3Listener.py +0 -0
  53. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/_antlr/Protobuf3Parser.py +0 -0
  54. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/_antlr/Protobuf3Visitor.py +0 -0
  55. {omextra-0.0.0.dev432/omextra/sql → omextra-0.0.0.dev434/omextra/specs/proto/_antlr}/__init__.py +0 -0
  56. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/nodes.py +0 -0
  57. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/specs/proto/parsing.py +0 -0
  58. {omextra-0.0.0.dev432/omextra/sql/parsing → omextra-0.0.0.dev434/omextra/sql}/__init__.py +0 -0
  59. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/sql/parsing/Minisql.g4 +0 -0
  60. {omextra-0.0.0.dev432/omextra/sql/parsing/_antlr → omextra-0.0.0.dev434/omextra/sql/parsing}/__init__.py +0 -0
  61. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/sql/parsing/_antlr/MinisqlLexer.py +0 -0
  62. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/sql/parsing/_antlr/MinisqlListener.py +0 -0
  63. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/sql/parsing/_antlr/MinisqlParser.py +0 -0
  64. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/sql/parsing/_antlr/MinisqlVisitor.py +0 -0
  65. {omextra-0.0.0.dev432/omextra/text → omextra-0.0.0.dev434/omextra/sql/parsing/_antlr}/__init__.py +0 -0
  66. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/sql/parsing/parsing.py +0 -0
  67. {omextra-0.0.0.dev432/omextra/text/antlr → omextra-0.0.0.dev434/omextra/text}/__init__.py +0 -0
  68. {omextra-0.0.0.dev432/omextra/text/antlr/cli → omextra-0.0.0.dev434/omextra/text/antlr}/__init__.py +0 -0
  69. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/text/antlr/cli/__main__.py +0 -0
  70. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/text/antlr/cli/cli.py +0 -0
  71. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/text/antlr/cli/consts.py +0 -0
  72. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra/text/antlr/cli/gen.py +0 -0
  73. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra.egg-info/dependency_links.txt +0 -0
  74. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra.egg-info/entry_points.txt +0 -0
  75. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/omextra.egg-info/top_level.txt +0 -0
  76. {omextra-0.0.0.dev432 → omextra-0.0.0.dev434}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omextra
3
- Version: 0.0.0.dev432
3
+ Version: 0.0.0.dev434
4
4
  Summary: omextra
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev432
17
+ Requires-Dist: omlish==0.0.0.dev434
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -0,0 +1,359 @@
1
+ """
2
+ A system for bridging sync and async code. Supports nesting / reentrancy. Robust, but not the most efficient
3
+ implementation - its primary usecase are heavy and init-time ops like lock management and connection establishment, not
4
+ so much for packet-level ops.
5
+
6
+ ==
7
+
8
+ The code is written in a very dumb, simple, explicit style to keep the bookkeeping center-stage and front-of-mind. To
9
+ quote kubernetes:
10
+
11
+ https://github.com/kubernetes/kubernetes/blob/60c4c2b2521fb454ce69dee737e3eb91a25e0535/pkg/controller/volume/persistentvolume/pv_controller.go#L60-L63
12
+
13
+ ==================================================================
14
+ PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE.
15
+ KEEP THE SPACE SHUTTLE FLYING.
16
+ ==================================================================
17
+
18
+ ==
19
+
20
+ TODO:
21
+ - reuse greenlet if nested somehow?
22
+ - test cancel
23
+
24
+ See:
25
+ - https://github.com/sqlalchemy/sqlalchemy/blob/21ea01eebe0350ad1185c7288dca61f363ebd2fe/lib/sqlalchemy/util/concurrency.py
26
+ - ( https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef )
27
+ - Baked into sqlalchemy and hard-bound to asyncio.
28
+ - https://github.com/oremanj/greenback/blob/ca69b023a9b7b58b715f3b1d78fc116e788a2c9f/greenback/_impl.py
29
+ - ( https://gist.github.com/oremanj/f18ef3e55b9487c2e93eee42232583f2 )
30
+ - Similar to this, but does horrible things with ctypes and is hard-bound to greenlet.
31
+ - https://github.com/fastapi/asyncer/blob/2a4b8ef2540ec687af13d3f361c4ed0cf0cb624d/asyncer/_main.py
32
+ - https://github.com/django/asgiref/blob/05ae3eee3fae4005ae4cfb0bb22d281725fabade/asgiref/sync.py
33
+ - Both are much heavier weight and hard-bound to threads.
34
+ """ # noqa
35
+ import functools # noqa
36
+ import itertools
37
+ import sys # noqa
38
+ import types
39
+ import typing as ta
40
+ import weakref
41
+
42
+ from omlish import check
43
+ from omlish import lang
44
+ from omlish import sync
45
+ from omlish.concurrent import threadlets
46
+
47
+
48
+ if ta.TYPE_CHECKING:
49
+ import asyncio
50
+
51
+ from omlish.asyncs import anyio as aiu
52
+
53
+ else:
54
+ asyncio = lang.proxy_import('asyncio')
55
+
56
+ aiu = lang.proxy_import('omlish.asyncs.anyio')
57
+
58
+
59
+ T = ta.TypeVar('T')
60
+
61
+
62
+ ##
63
+
64
+
65
+ def trivial_s_to_a(fn):
66
+ async def inner(*args, **kwargs):
67
+ return fn(*args, **kwargs)
68
+ return inner
69
+
70
+
71
+ def trivial_a_to_s(fn):
72
+ def inner(*args, **kwargs):
73
+ return lang.sync_await(fn(*args, **kwargs))
74
+ return inner
75
+
76
+
77
+ ##
78
+ # https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef
79
+
80
+
81
+ _THREADLETS_IMPL = threadlets.GreenletThreadlets
82
+ # from ..concurrent.tests.real import RealThreadlets
83
+ # _THREADLETS_IMPL = RealThreadlets
84
+
85
+ _THREADLETS = sync.LazyFn(lambda: _THREADLETS_IMPL())
86
+
87
+
88
+ def _threadlets() -> threadlets.Threadlets:
89
+ return _THREADLETS.get()
90
+
91
+
92
+ #
93
+
94
+
95
+ class BridgeAwaitRequiredError(Exception):
96
+ pass
97
+
98
+
99
+ class MissingBridgeThreadletError(Exception):
100
+ pass
101
+
102
+
103
+ class UnexpectedBridgeNestingError(Exception):
104
+ def __init__(self, *args, **kwargs):
105
+ super().__init__(*args, **kwargs)
106
+ # breakpoint()
107
+
108
+
109
+ #
110
+
111
+
112
+ _DEBUG_PRINT: ta.Callable[..., None] | None = None
113
+ # _DEBUG_PRINT = functools.partial(print, file=sys.stderr) # noqa
114
+
115
+ _TRACK_TRANSITION_OBJS = False
116
+
117
+
118
+ #
119
+
120
+
121
+ _BRIDGE_TRANSITIONS_SEQ = itertools.count()
122
+
123
+
124
+ class _BridgeTransition(ta.NamedTuple):
125
+ seq: int
126
+ a_to_s: bool
127
+
128
+ obj_cls: type
129
+ obj_id: int
130
+
131
+ obj: ta.Any
132
+
133
+
134
+ def _make_transition(seq: int, a_to_s: bool, obj: ta.Any) -> _BridgeTransition:
135
+ return _BridgeTransition(seq, a_to_s, obj.__class__, id(obj), (obj if _TRACK_TRANSITION_OBJS else None))
136
+
137
+
138
+ _BRIDGED_TASKS: ta.MutableMapping[ta.Any, list[_BridgeTransition]] = weakref.WeakKeyDictionary()
139
+
140
+ _BRIDGE_THREADLET_ATTR = f'__{__package__.replace(".", "__")}__bridge_threadlet__'
141
+
142
+
143
+ def _push_transition(a_to_s: bool, l: list[_BridgeTransition], t: _BridgeTransition) -> _BridgeTransition:
144
+ l.append(t)
145
+ if _DEBUG_PRINT:
146
+ _DEBUG_PRINT(f'_push_transition: {a_to_s=} {id(l)=} {t=}')
147
+ return t
148
+
149
+
150
+ def _pop_transition(a_to_s: bool, l: list[_BridgeTransition]) -> _BridgeTransition:
151
+ t = l.pop()
152
+ if _DEBUG_PRINT:
153
+ _DEBUG_PRINT(f'_pop_transition: {a_to_s=} {id(l)=} {t=}')
154
+ return t
155
+
156
+
157
+ def _get_transitions() -> list[_BridgeTransition]:
158
+ l: list[_BridgeTransition] = []
159
+
160
+ if (t := aiu.get_current_backend_task()) is not None:
161
+ try:
162
+ tl = _BRIDGED_TASKS[t]
163
+ except KeyError:
164
+ pass
165
+ else:
166
+ l.extend(tl)
167
+
168
+ g = _threadlets().get_current()
169
+ try:
170
+ gl = getattr(g.underlying, _BRIDGE_THREADLET_ATTR)
171
+ except AttributeError:
172
+ pass
173
+ else:
174
+ l.extend(gl)
175
+
176
+ l.sort(key=lambda t: (t.seq, t.a_to_s))
177
+ return l
178
+
179
+
180
+ def is_in_bridge() -> bool:
181
+ if _DEBUG_PRINT:
182
+ _DEBUG_PRINT(_get_transitions())
183
+
184
+ if (t := aiu.get_current_backend_task()) is not None:
185
+ try:
186
+ tl = _BRIDGED_TASKS[t]
187
+ except KeyError:
188
+ last_t = None
189
+ else:
190
+ if tl:
191
+ last_t = tl[-1]
192
+ else:
193
+ last_t = None
194
+ else:
195
+ last_t = None
196
+
197
+ g = _threadlets().get_current()
198
+ try:
199
+ gl = getattr(g.underlying, _BRIDGE_THREADLET_ATTR)
200
+ except AttributeError:
201
+ last_g = None
202
+ else:
203
+ if gl:
204
+ last_g = gl[-1]
205
+ else:
206
+ last_g = None
207
+
208
+ if last_t is None:
209
+ if last_g is None:
210
+ return False
211
+ o = last_g
212
+ else: # noqa
213
+ if last_g is None or last_g.seq < last_t.seq:
214
+ o = last_t
215
+ else:
216
+ o = last_g
217
+
218
+ in_a = (t is not None)
219
+
220
+ if _DEBUG_PRINT:
221
+ _DEBUG_PRINT(f'{o.a_to_s=} {in_a=}')
222
+
223
+ return in_a != o.a_to_s
224
+
225
+
226
+ def _safe_cancel_awaitable(awaitable: ta.Awaitable[ta.Any]) -> None:
227
+ # https://docs.python.org/3/reference/datamodel.html#coroutine.close
228
+ if asyncio.iscoroutine(awaitable):
229
+ awaitable.close() # noqa
230
+
231
+
232
+ def s_to_a_await(awaitable: ta.Awaitable[T]) -> T:
233
+ g = _threadlets().get_current()
234
+
235
+ if not getattr(g.underlying, _BRIDGE_THREADLET_ATTR, False):
236
+ _safe_cancel_awaitable(awaitable)
237
+ raise MissingBridgeThreadletError
238
+
239
+ return check.not_none(g.parent).switch(awaitable)
240
+
241
+
242
+ def s_to_a(fn, *, require_await=False):
243
+ @types.coroutine
244
+ def outer(*args, **kwargs):
245
+ def inner():
246
+ try:
247
+ return fn(*args, **kwargs)
248
+ finally:
249
+ if (gl2 := getattr(g.underlying, _BRIDGE_THREADLET_ATTR)) is not gl: # noqa
250
+ raise UnexpectedBridgeNestingError
251
+ if (cur_g := _pop_transition(False, gl)) is not added_g: # noqa
252
+ raise UnexpectedBridgeNestingError
253
+ if gl:
254
+ raise UnexpectedBridgeNestingError
255
+
256
+ seq = next(_BRIDGE_TRANSITIONS_SEQ)
257
+
258
+ g = _threadlets().spawn(inner)
259
+ setattr(g.underlying, _BRIDGE_THREADLET_ATTR, gl := []) # type: ignore
260
+ added_g = _push_transition(False, gl, _make_transition(seq, False, g))
261
+
262
+ if (t := aiu.get_current_backend_task()) is not None:
263
+ try:
264
+ tl = _BRIDGED_TASKS[t]
265
+ except KeyError:
266
+ tl = _BRIDGED_TASKS[t] = []
267
+ added_t = _push_transition(False, tl, _make_transition(seq, False, g))
268
+
269
+ try:
270
+ result: ta.Any = g.switch()
271
+ switch_occurred = False
272
+ while not g.dead:
273
+ switch_occurred = True
274
+ try:
275
+ value = yield result
276
+ except BaseException as e: # noqa
277
+ result = g.throw(e)
278
+ else:
279
+ result = g.switch(value)
280
+
281
+ if require_await and not switch_occurred:
282
+ raise BridgeAwaitRequiredError
283
+
284
+ return result
285
+
286
+ finally:
287
+ if t is not None:
288
+ if (tl2 := _BRIDGED_TASKS[t]) is not tl: # noqa
289
+ raise UnexpectedBridgeNestingError
290
+ if (cur_t := _pop_transition(False, tl)) is not added_t: # noqa
291
+ raise UnexpectedBridgeNestingError
292
+
293
+ return outer
294
+
295
+
296
+ def a_to_s(fn):
297
+ def inner(*args, **kwargs):
298
+ seq = next(_BRIDGE_TRANSITIONS_SEQ)
299
+
300
+ if (t := aiu.get_current_backend_task()) is not None:
301
+ try:
302
+ tl = _BRIDGED_TASKS[t]
303
+ except KeyError:
304
+ tl = _BRIDGED_TASKS[t] = []
305
+ added_t = _push_transition(True, tl, _make_transition(seq, True, t))
306
+ else:
307
+ added_t = None
308
+
309
+ g = _threadlets().get_current()
310
+ try:
311
+ gl = getattr(g.underlying, _BRIDGE_THREADLET_ATTR)
312
+ except AttributeError:
313
+ setattr(g.underlying, _BRIDGE_THREADLET_ATTR, gl := [])
314
+ added_g = _push_transition(True, gl, _make_transition(seq, True, g))
315
+
316
+ try:
317
+ ret = missing = object()
318
+
319
+ async def gate():
320
+ nonlocal ret
321
+ ret = await fn(*args, **kwargs)
322
+
323
+ cr = gate()
324
+ sv = None
325
+ he = False
326
+ try:
327
+ while True:
328
+ if not he:
329
+ try:
330
+ sv = cr.send(sv)
331
+ except StopIteration:
332
+ break
333
+ he = False
334
+
335
+ if ret is missing or cr.cr_await is not None or cr.cr_running:
336
+ try:
337
+ sv = s_to_a_await(sv) # type: ignore
338
+ except BaseException as e: # noqa
339
+ sv = cr.throw(e)
340
+ he = True
341
+
342
+ finally:
343
+ cr.close()
344
+
345
+ finally:
346
+ if t is not None:
347
+ if (tl2 := _BRIDGED_TASKS[t]) is not tl: # noqa
348
+ raise UnexpectedBridgeNestingError
349
+ if (cur_t := _pop_transition(True, tl)) is not added_t: # noqa
350
+ raise UnexpectedBridgeNestingError
351
+
352
+ if (gl2 := getattr(g.underlying, _BRIDGE_THREADLET_ATTR)) is not gl: # noqa
353
+ raise UnexpectedBridgeNestingError
354
+ if (cur_g := _pop_transition(True, gl)) is not added_g: # noqa
355
+ raise UnexpectedBridgeNestingError
356
+
357
+ return ret
358
+
359
+ return inner
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omextra
3
- Version: 0.0.0.dev432
3
+ Version: 0.0.0.dev434
4
4
  Summary: omextra
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev432
17
+ Requires-Dist: omlish==0.0.0.dev434
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -13,6 +13,8 @@ omextra.egg-info/dependency_links.txt
13
13
  omextra.egg-info/entry_points.txt
14
14
  omextra.egg-info/requires.txt
15
15
  omextra.egg-info/top_level.txt
16
+ omextra/asyncs/__init__.py
17
+ omextra/asyncs/bridge.py
16
18
  omextra/formats/__init__.py
17
19
  omextra/formats/json/Json.g4
18
20
  omextra/formats/json/__init__.py
@@ -0,0 +1 @@
1
+ omlish==0.0.0.dev434
@@ -13,7 +13,7 @@ urls = {source = 'https://github.com/wrmsr/omlish'}
13
13
  license = 'BSD-3-Clause'
14
14
  readme = 'README.md'
15
15
  requires-python = '>=3.13'
16
- version = '0.0.0.dev432'
16
+ version = '0.0.0.dev434'
17
17
  classifiers = [
18
18
  'Development Status :: 2 - Pre-Alpha',
19
19
  'Intended Audience :: Developers',
@@ -24,7 +24,7 @@ classifiers = [
24
24
  ]
25
25
  description = 'omextra'
26
26
  dependencies = [
27
- 'omlish == 0.0.0.dev432',
27
+ 'omlish == 0.0.0.dev434',
28
28
  ]
29
29
 
30
30
  [project.optional-dependencies]
@@ -1 +0,0 @@
1
- omlish==0.0.0.dev432
File without changes
File without changes
File without changes