omextra 0.0.0.dev424__py3-none-any.whl → 0.0.0.dev426__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.
- omextra/.omlish-manifests.json +14 -0
- omextra/__about__.py +3 -1
- omextra/defs.py +216 -0
- omextra/dynamic.py +219 -0
- omextra/formats/__init__.py +0 -0
- omextra/formats/json/Json.g4 +77 -0
- omextra/formats/json/__init__.py +0 -0
- omextra/formats/json/_antlr/JsonLexer.py +109 -0
- omextra/formats/json/_antlr/JsonListener.py +61 -0
- omextra/formats/json/_antlr/JsonParser.py +457 -0
- omextra/formats/json/_antlr/JsonVisitor.py +42 -0
- omextra/formats/json/_antlr/__init__.py +0 -0
- omextra/io/__init__.py +0 -0
- omextra/io/trampoline.py +289 -0
- omextra/specs/__init__.py +0 -0
- omextra/specs/irc/__init__.py +0 -0
- omextra/specs/irc/messages/__init__.py +0 -0
- omextra/specs/irc/messages/base.py +50 -0
- omextra/specs/irc/messages/formats.py +92 -0
- omextra/specs/irc/messages/messages.py +775 -0
- omextra/specs/irc/messages/parsing.py +99 -0
- omextra/specs/irc/numerics/__init__.py +0 -0
- omextra/specs/irc/numerics/formats.py +97 -0
- omextra/specs/irc/numerics/numerics.py +865 -0
- omextra/specs/irc/numerics/types.py +59 -0
- omextra/specs/irc/protocol/LICENSE +11 -0
- omextra/specs/irc/protocol/__init__.py +61 -0
- omextra/specs/irc/protocol/consts.py +6 -0
- omextra/specs/irc/protocol/errors.py +30 -0
- omextra/specs/irc/protocol/message.py +21 -0
- omextra/specs/irc/protocol/nuh.py +55 -0
- omextra/specs/irc/protocol/parsing.py +158 -0
- omextra/specs/irc/protocol/rendering.py +153 -0
- omextra/specs/irc/protocol/tags.py +102 -0
- omextra/specs/irc/protocol/utils.py +30 -0
- omextra/specs/proto/Protobuf3.g4 +396 -0
- omextra/specs/proto/__init__.py +0 -0
- omextra/specs/proto/_antlr/Protobuf3Lexer.py +340 -0
- omextra/specs/proto/_antlr/Protobuf3Listener.py +448 -0
- omextra/specs/proto/_antlr/Protobuf3Parser.py +3909 -0
- omextra/specs/proto/_antlr/Protobuf3Visitor.py +257 -0
- omextra/specs/proto/_antlr/__init__.py +0 -0
- omextra/specs/proto/nodes.py +54 -0
- omextra/specs/proto/parsing.py +98 -0
- omextra/sql/__init__.py +0 -0
- omextra/sql/parsing/Minisql.g4 +292 -0
- omextra/sql/parsing/__init__.py +0 -0
- omextra/sql/parsing/_antlr/MinisqlLexer.py +322 -0
- omextra/sql/parsing/_antlr/MinisqlListener.py +511 -0
- omextra/sql/parsing/_antlr/MinisqlParser.py +3763 -0
- omextra/sql/parsing/_antlr/MinisqlVisitor.py +292 -0
- omextra/sql/parsing/_antlr/__init__.py +0 -0
- omextra/sql/parsing/parsing.py +120 -0
- omextra/text/__init__.py +0 -0
- omextra/text/antlr/__init__.py +0 -0
- omextra/text/antlr/cli/__init__.py +0 -0
- omextra/text/antlr/cli/__main__.py +11 -0
- omextra/text/antlr/cli/cli.py +62 -0
- omextra/text/antlr/cli/consts.py +7 -0
- omextra/text/antlr/cli/gen.py +193 -0
- {omextra-0.0.0.dev424.dist-info → omextra-0.0.0.dev426.dist-info}/METADATA +2 -3
- omextra-0.0.0.dev426.dist-info/RECORD +67 -0
- omextra/.manifests.json +0 -1
- omextra-0.0.0.dev424.dist-info/RECORD +0 -9
- {omextra-0.0.0.dev424.dist-info → omextra-0.0.0.dev426.dist-info}/WHEEL +0 -0
- {omextra-0.0.0.dev424.dist-info → omextra-0.0.0.dev426.dist-info}/entry_points.txt +0 -0
- {omextra-0.0.0.dev424.dist-info → omextra-0.0.0.dev426.dist-info}/licenses/LICENSE +0 -0
- {omextra-0.0.0.dev424.dist-info → omextra-0.0.0.dev426.dist-info}/top_level.txt +0 -0
omextra/io/trampoline.py
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
import abc
|
2
|
+
import contextlib
|
3
|
+
import io
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from omlish import check
|
7
|
+
from omlish import lang
|
8
|
+
from omlish.concurrent import threadlets as tls
|
9
|
+
from omlish.sync import ConditionDeque
|
10
|
+
|
11
|
+
|
12
|
+
if ta.TYPE_CHECKING:
|
13
|
+
import threading
|
14
|
+
|
15
|
+
from omlish.io import pyio # noqa
|
16
|
+
|
17
|
+
else:
|
18
|
+
threading = lang.proxy_import('threading')
|
19
|
+
|
20
|
+
pyio = lang.proxy_import('omlish.io.pyio')
|
21
|
+
|
22
|
+
|
23
|
+
T = ta.TypeVar('T')
|
24
|
+
|
25
|
+
BytesLike: ta.TypeAlias = ta.Any
|
26
|
+
|
27
|
+
BufferedReader: ta.TypeAlias = io.BufferedReader
|
28
|
+
# BufferedReader: ta.TypeAlias = pyio.BufferedReader
|
29
|
+
|
30
|
+
|
31
|
+
##
|
32
|
+
|
33
|
+
|
34
|
+
class ProxyReadFile:
|
35
|
+
def __init__(self, read: ta.Callable[[int], bytes | BaseException]) -> None:
|
36
|
+
super().__init__()
|
37
|
+
|
38
|
+
self._read = read
|
39
|
+
self._eof = False
|
40
|
+
self._closed = False
|
41
|
+
|
42
|
+
#
|
43
|
+
|
44
|
+
def read(self, n: int, /) -> bytes:
|
45
|
+
if self._closed:
|
46
|
+
raise RuntimeError('Closed')
|
47
|
+
if self._eof:
|
48
|
+
return b''
|
49
|
+
d = self._read(n)
|
50
|
+
if isinstance(d, BaseException):
|
51
|
+
raise d
|
52
|
+
if not d:
|
53
|
+
self._eof = True
|
54
|
+
return d
|
55
|
+
|
56
|
+
def readall(self, *args, **kwargs):
|
57
|
+
raise TypeError # FIXME
|
58
|
+
|
59
|
+
def readinto(self, b: BytesLike) -> int | None:
|
60
|
+
d = self.read(len(b))
|
61
|
+
if d:
|
62
|
+
b[:len(d)] = d
|
63
|
+
return len(d)
|
64
|
+
|
65
|
+
#
|
66
|
+
|
67
|
+
def readable(self) -> bool:
|
68
|
+
return not (self._eof or self._closed)
|
69
|
+
|
70
|
+
@property
|
71
|
+
def closed(self) -> bool:
|
72
|
+
return self._closed
|
73
|
+
|
74
|
+
def close(self) -> None:
|
75
|
+
self._closed = True
|
76
|
+
|
77
|
+
#
|
78
|
+
|
79
|
+
def flush(self) -> None:
|
80
|
+
pass
|
81
|
+
|
82
|
+
def seekable(self) -> bool:
|
83
|
+
return False
|
84
|
+
|
85
|
+
def seek(self, n: int, /) -> ta.Any:
|
86
|
+
raise TypeError
|
87
|
+
|
88
|
+
|
89
|
+
##
|
90
|
+
|
91
|
+
|
92
|
+
class NeedMore(lang.Marker):
|
93
|
+
pass
|
94
|
+
|
95
|
+
|
96
|
+
class Exited(lang.Marker):
|
97
|
+
pass
|
98
|
+
|
99
|
+
|
100
|
+
class Shutdown(BaseException): # noqa
|
101
|
+
pass
|
102
|
+
|
103
|
+
|
104
|
+
IoTrampolineTarget: ta.TypeAlias = ta.Callable[[io.BufferedReader], ta.ContextManager[ta.Callable[[], bytes]]]
|
105
|
+
|
106
|
+
|
107
|
+
class IoTrampoline(lang.Abstract):
|
108
|
+
def __init__(
|
109
|
+
self,
|
110
|
+
target: IoTrampolineTarget,
|
111
|
+
*,
|
112
|
+
buffer_size: int | None = None,
|
113
|
+
) -> None:
|
114
|
+
super().__init__()
|
115
|
+
|
116
|
+
self._target = target
|
117
|
+
self._buffer_size = buffer_size
|
118
|
+
|
119
|
+
def _make_buffered_reader(self, raw: ta.Any) -> io.BufferedReader:
|
120
|
+
return io.BufferedReader(
|
121
|
+
raw,
|
122
|
+
**(dict(buffer_size=self._buffer_size) if self._buffer_size is not None else {}),
|
123
|
+
)
|
124
|
+
|
125
|
+
@abc.abstractmethod
|
126
|
+
def close(self, timeout: float | None = None) -> None:
|
127
|
+
raise NotImplementedError
|
128
|
+
|
129
|
+
@abc.abstractmethod
|
130
|
+
def __enter__(self) -> ta.Self:
|
131
|
+
raise NotImplementedError
|
132
|
+
|
133
|
+
@abc.abstractmethod
|
134
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
135
|
+
raise NotImplementedError
|
136
|
+
|
137
|
+
@abc.abstractmethod
|
138
|
+
def feed(self, *data: bytes) -> ta.Iterator[bytes]:
|
139
|
+
raise NotImplementedError
|
140
|
+
|
141
|
+
|
142
|
+
#
|
143
|
+
|
144
|
+
|
145
|
+
class ThreadIoTrampoline(IoTrampoline):
|
146
|
+
def __init__(self, target: IoTrampolineTarget, **kwargs: ta.Any) -> None:
|
147
|
+
super().__init__(target, **kwargs)
|
148
|
+
|
149
|
+
self._in: ConditionDeque[bytes | BaseException] = ConditionDeque()
|
150
|
+
self._out: ConditionDeque[
|
151
|
+
bytes | # noqa
|
152
|
+
type[NeedMore] |
|
153
|
+
type[Exited] |
|
154
|
+
BaseException
|
155
|
+
] = ConditionDeque()
|
156
|
+
|
157
|
+
self._proxy = ProxyReadFile(self._read)
|
158
|
+
|
159
|
+
self._thread = threading.Thread(target=self._thread_proc, daemon=True)
|
160
|
+
|
161
|
+
#
|
162
|
+
|
163
|
+
def close(self, timeout: float | None = None) -> None:
|
164
|
+
if not self._thread.is_alive():
|
165
|
+
return
|
166
|
+
self._out.push(Shutdown())
|
167
|
+
self._thread.join(timeout)
|
168
|
+
if self._thread.is_alive():
|
169
|
+
if timeout is not None:
|
170
|
+
raise TimeoutError
|
171
|
+
else:
|
172
|
+
raise RuntimeError('Failed to join thread')
|
173
|
+
|
174
|
+
#
|
175
|
+
|
176
|
+
def __enter__(self) -> ta.Self:
|
177
|
+
self._thread.start()
|
178
|
+
return self
|
179
|
+
|
180
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
181
|
+
self.close()
|
182
|
+
|
183
|
+
#
|
184
|
+
|
185
|
+
def _read(self, n: int, /) -> bytes | BaseException:
|
186
|
+
return self._in.pop(if_empty=lambda: self._out.push(NeedMore))
|
187
|
+
|
188
|
+
def _thread_proc(self) -> None:
|
189
|
+
try:
|
190
|
+
with contextlib.closing(self._make_buffered_reader(self._proxy)) as bf: # noqa
|
191
|
+
with self._target(bf) as read:
|
192
|
+
while out := read():
|
193
|
+
self._out.push(out)
|
194
|
+
self._out.push(out)
|
195
|
+
except BaseException as e:
|
196
|
+
self._out.push(e)
|
197
|
+
raise
|
198
|
+
finally:
|
199
|
+
self._out.push(Exited)
|
200
|
+
|
201
|
+
def feed(self, *data: bytes) -> ta.Iterator[bytes]:
|
202
|
+
self._in.push(*data)
|
203
|
+
while True:
|
204
|
+
e = self._out.pop()
|
205
|
+
if e is NeedMore:
|
206
|
+
break
|
207
|
+
elif isinstance(e, BaseException):
|
208
|
+
raise e
|
209
|
+
elif e is Exited:
|
210
|
+
raise RuntimeError('IO thread exited')
|
211
|
+
elif isinstance(e, bytes):
|
212
|
+
yield e
|
213
|
+
if not e:
|
214
|
+
return
|
215
|
+
else:
|
216
|
+
raise TypeError(e)
|
217
|
+
|
218
|
+
|
219
|
+
#
|
220
|
+
|
221
|
+
|
222
|
+
class ThreadletIoTrampoline(IoTrampoline):
|
223
|
+
def __init__(
|
224
|
+
self,
|
225
|
+
target: IoTrampolineTarget,
|
226
|
+
threadlets: tls.Threadlets = tls.GREENLET_THREADLETS,
|
227
|
+
** kwargs: ta.Any,
|
228
|
+
) -> None:
|
229
|
+
super().__init__(target, **kwargs)
|
230
|
+
|
231
|
+
self._proxy = ProxyReadFile(self._read)
|
232
|
+
self._tl: tls.Threadlet = threadlets.spawn(self._g_proc)
|
233
|
+
|
234
|
+
#
|
235
|
+
|
236
|
+
def close(self, timeout: float | None = None) -> None:
|
237
|
+
if self._tl.dead:
|
238
|
+
return
|
239
|
+
out = self._tl.switch(Shutdown())
|
240
|
+
if out is not Exited or not self._tl.dead:
|
241
|
+
raise RuntimeError
|
242
|
+
|
243
|
+
#
|
244
|
+
|
245
|
+
def __enter__(self) -> ta.Self:
|
246
|
+
out = self._tl.switch()
|
247
|
+
if out is not NeedMore:
|
248
|
+
raise RuntimeError
|
249
|
+
return self
|
250
|
+
|
251
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
252
|
+
self.close()
|
253
|
+
|
254
|
+
#
|
255
|
+
|
256
|
+
def _read(self, n: int, /) -> bytes:
|
257
|
+
out = check.not_none(self._tl.parent).switch(NeedMore)
|
258
|
+
return out
|
259
|
+
|
260
|
+
def _g_proc(self) -> ta.Any:
|
261
|
+
try:
|
262
|
+
with contextlib.closing(self._make_buffered_reader(self._proxy)) as bf: # noqa
|
263
|
+
with self._target(bf) as read:
|
264
|
+
while out := read():
|
265
|
+
e = check.not_none(self._tl.parent).switch(out)
|
266
|
+
if e is not NeedMore:
|
267
|
+
raise TypeError(e) # noqa
|
268
|
+
e = check.not_none(self._tl.parent).switch(out)
|
269
|
+
if not isinstance(e, Shutdown):
|
270
|
+
raise TypeError(e) # noqa
|
271
|
+
return Exited
|
272
|
+
except BaseException as e:
|
273
|
+
check.not_none(self._tl.parent).throw(e)
|
274
|
+
raise
|
275
|
+
|
276
|
+
def feed(self, *data: bytes) -> ta.Iterator[bytes]:
|
277
|
+
i: bytes | type[NeedMore]
|
278
|
+
for i in data:
|
279
|
+
while True:
|
280
|
+
e = self._tl.switch(i)
|
281
|
+
i = NeedMore
|
282
|
+
if e is NeedMore:
|
283
|
+
break
|
284
|
+
elif isinstance(e, bytes):
|
285
|
+
yield e
|
286
|
+
if not e:
|
287
|
+
return
|
288
|
+
else:
|
289
|
+
raise TypeError(e)
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from omlish import check
|
4
|
+
from omlish import dataclasses as dc
|
5
|
+
from omlish.funcs import pairs as fps
|
6
|
+
|
7
|
+
from ..numerics import numerics as nr
|
8
|
+
from .formats import MessageFormat
|
9
|
+
from .formats import MessageParamsUnpacker
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
class Message(dc.Case):
|
16
|
+
FORMAT: ta.ClassVar[MessageFormat]
|
17
|
+
REPLIES: ta.ClassVar[ta.Collection[nr.NumericReply]] = ()
|
18
|
+
|
19
|
+
|
20
|
+
##
|
21
|
+
|
22
|
+
|
23
|
+
def list_pair_params_unpacker(
|
24
|
+
kwarg: str,
|
25
|
+
key_param: str,
|
26
|
+
value_param: str,
|
27
|
+
) -> MessageParamsUnpacker:
|
28
|
+
def forward(params: ta.Mapping[str, str]) -> ta.Mapping[str, ta.Any]:
|
29
|
+
out: dict = dict(params)
|
30
|
+
ks = out.pop(key_param)
|
31
|
+
vs = out.pop(value_param, None)
|
32
|
+
if vs is not None:
|
33
|
+
out[kwarg] = list(zip(ks, vs, strict=True))
|
34
|
+
else:
|
35
|
+
out[kwarg] = ks
|
36
|
+
return out
|
37
|
+
|
38
|
+
def backward(kwargs: ta.Mapping[str, ta.Any]) -> ta.Mapping[str, str]:
|
39
|
+
out: dict = dict(kwargs)
|
40
|
+
ts = out.pop(kwarg)
|
41
|
+
is_ts = check.single({isinstance(e, tuple) for e in ts})
|
42
|
+
if is_ts:
|
43
|
+
ks, vs = zip(*ts)
|
44
|
+
out[key_param] = ks
|
45
|
+
out[value_param] = vs
|
46
|
+
else:
|
47
|
+
out[key_param] = ts
|
48
|
+
return out
|
49
|
+
|
50
|
+
return fps.of(forward, backward)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import enum
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish import check
|
5
|
+
from omlish import dataclasses as dc
|
6
|
+
from omlish import lang
|
7
|
+
from omlish.funcs import pairs as fps
|
8
|
+
|
9
|
+
|
10
|
+
MessageParamsUnpacker: ta.TypeAlias = fps.FnPair[
|
11
|
+
ta.Mapping[str, str], # params
|
12
|
+
ta.Mapping[str, ta.Any], # kwargs
|
13
|
+
]
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
class MessageFormat(dc.Frozen, final=True):
|
20
|
+
name: str
|
21
|
+
|
22
|
+
class Param(dc.Case):
|
23
|
+
@classmethod
|
24
|
+
def of(cls, obj: ta.Any) -> 'MessageFormat.Param':
|
25
|
+
if isinstance(obj, MessageFormat.Param):
|
26
|
+
return obj
|
27
|
+
|
28
|
+
elif isinstance(obj, str):
|
29
|
+
s = check.non_empty_str(obj)
|
30
|
+
|
31
|
+
optional = False
|
32
|
+
if s.startswith('?'):
|
33
|
+
optional = True
|
34
|
+
s = s[1:]
|
35
|
+
|
36
|
+
arity = MessageFormat.KwargParam.Arity.SINGLE
|
37
|
+
if s.startswith('*'):
|
38
|
+
arity = MessageFormat.KwargParam.Arity.VARIADIC
|
39
|
+
s = s[1:]
|
40
|
+
|
41
|
+
elif s.startswith(','):
|
42
|
+
arity = MessageFormat.KwargParam.Arity.COMMA_LIST
|
43
|
+
s = s[1:]
|
44
|
+
|
45
|
+
return MessageFormat.KwargParam(
|
46
|
+
s,
|
47
|
+
optional=optional,
|
48
|
+
arity=arity,
|
49
|
+
)
|
50
|
+
|
51
|
+
else:
|
52
|
+
raise TypeError(obj)
|
53
|
+
|
54
|
+
class KwargParam(Param):
|
55
|
+
name: str = dc.xfield(validate=lang.is_ident)
|
56
|
+
|
57
|
+
optional: bool = False
|
58
|
+
|
59
|
+
class Arity(enum.Enum):
|
60
|
+
SINGLE = enum.auto() # <foo>
|
61
|
+
VARIADIC = enum.auto() # <foo>{ <foo>}
|
62
|
+
COMMA_LIST = enum.auto() # <foo>{,<foo>}
|
63
|
+
|
64
|
+
arity: Arity = Arity.SINGLE
|
65
|
+
|
66
|
+
class LiteralParam(Param):
|
67
|
+
text: str
|
68
|
+
|
69
|
+
params: ta.Sequence[Param]
|
70
|
+
|
71
|
+
_: dc.KW_ONLY
|
72
|
+
|
73
|
+
unpack_params: MessageParamsUnpacker | None = None
|
74
|
+
|
75
|
+
@dc.init
|
76
|
+
def _validate_params(self) -> None:
|
77
|
+
kws = [p for p in self.params if isinstance(p, MessageFormat.KwargParam)]
|
78
|
+
check.unique(p.name for p in kws)
|
79
|
+
check.state(all(p.arity is not MessageFormat.KwargParam.Arity.VARIADIC for p in kws[:-1]))
|
80
|
+
|
81
|
+
@classmethod
|
82
|
+
def of(
|
83
|
+
cls,
|
84
|
+
name: str,
|
85
|
+
*params: ta.Any,
|
86
|
+
**kwargs: ta.Any,
|
87
|
+
) -> 'MessageFormat':
|
88
|
+
return cls(
|
89
|
+
name,
|
90
|
+
[MessageFormat.Param.of(p) for p in params],
|
91
|
+
**kwargs,
|
92
|
+
)
|