omlish 0.0.0.dev22__py3-none-any.whl → 0.0.0.dev24__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.
- omlish/__about__.py +10 -3
- omlish/asyncs/bridge.py +3 -0
- omlish/bootstrap/__init__.py +39 -0
- omlish/bootstrap/base.py +3 -1
- omlish/bootstrap/diag.py +48 -1
- omlish/bootstrap/main.py +2 -1
- omlish/bootstrap/marshal.py +18 -0
- omlish/check.py +305 -37
- omlish/collections/__init__.py +2 -2
- omlish/collections/utils.py +3 -3
- omlish/concurrent/threadlets.py +5 -0
- omlish/dataclasses/__init__.py +1 -0
- omlish/dataclasses/impl/init.py +10 -2
- omlish/dataclasses/impl/metadata.py +3 -3
- omlish/dataclasses/impl/reflect.py +1 -1
- omlish/dataclasses/utils.py +16 -3
- omlish/diag/asts.py +132 -0
- omlish/diag/pycharm.py +139 -0
- omlish/docker.py +19 -11
- omlish/genmachine.py +59 -0
- omlish/graphs/trees.py +1 -1
- omlish/lang/__init__.py +13 -1
- omlish/lang/cached.py +5 -2
- omlish/lang/descriptors.py +33 -16
- omlish/lang/resources.py +60 -0
- omlish/lite/logs.py +133 -4
- omlish/logs/__init__.py +17 -2
- omlish/logs/configs.py +13 -1
- omlish/logs/formatters.py +0 -1
- omlish/marshal/__init__.py +6 -0
- omlish/marshal/base64.py +4 -0
- omlish/marshal/helpers.py +27 -0
- omlish/marshal/primitives.py +6 -0
- omlish/marshal/standard.py +4 -0
- omlish/marshal/unions.py +101 -0
- omlish/matchfns.py +3 -3
- omlish/specs/jsonschema/keywords/base.py +2 -2
- omlish/specs/jsonschema/keywords/parse.py +1 -1
- omlish/sql/__init__.py +18 -0
- omlish/sql/qualifiedname.py +82 -0
- omlish/stats.py +1 -0
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev24.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev24.dist-info}/RECORD +46 -39
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev24.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev24.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev24.dist-info}/top_level.txt +0 -0
omlish/lang/resources.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
import functools
|
2
|
+
import importlib.resources
|
3
|
+
import os.path
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
|
7
|
+
class RelativeResource(ta.NamedTuple):
|
8
|
+
name: str
|
9
|
+
is_file: bool
|
10
|
+
read_bytes: ta.Callable[[], bytes]
|
11
|
+
|
12
|
+
|
13
|
+
def get_relative_resources(
|
14
|
+
prefix: str | None = None,
|
15
|
+
*,
|
16
|
+
globals: ta.Mapping[str, ta.Any] | None = None, # noqa
|
17
|
+
package: str | None = None,
|
18
|
+
file: str | None = None,
|
19
|
+
) -> ta.Mapping[str, RelativeResource]:
|
20
|
+
if globals is not None:
|
21
|
+
if not package:
|
22
|
+
package = globals.get('__package__')
|
23
|
+
if not file:
|
24
|
+
file = globals.get('__file__')
|
25
|
+
|
26
|
+
lst: list[RelativeResource] = []
|
27
|
+
|
28
|
+
if package:
|
29
|
+
anchor = package
|
30
|
+
if prefix:
|
31
|
+
anchor += '.' + prefix.replace(os.sep, '.')
|
32
|
+
|
33
|
+
for pf in importlib.resources.files(anchor).iterdir():
|
34
|
+
lst.append(RelativeResource(
|
35
|
+
name=pf.name,
|
36
|
+
is_file=pf.is_file(),
|
37
|
+
read_bytes=pf.read_bytes if pf.is_file() else None, # type: ignore
|
38
|
+
))
|
39
|
+
|
40
|
+
elif file:
|
41
|
+
path = os.path.dirname(file)
|
42
|
+
if prefix:
|
43
|
+
path = os.path.join(path, prefix.replace('.', os.sep))
|
44
|
+
|
45
|
+
def _read_file(fp: str) -> bytes:
|
46
|
+
with open(fp, 'rb') as f:
|
47
|
+
return f.read()
|
48
|
+
|
49
|
+
for ff in os.listdir(path):
|
50
|
+
ff = os.path.join(path, ff)
|
51
|
+
lst.append(RelativeResource(
|
52
|
+
name=os.path.basename(ff),
|
53
|
+
is_file=os.path.isfile(ff),
|
54
|
+
read_bytes=functools.partial(_read_file, ff),
|
55
|
+
))
|
56
|
+
|
57
|
+
else:
|
58
|
+
raise RuntimeError('no package or file specified')
|
59
|
+
|
60
|
+
return {r.name: r for r in lst}
|
omlish/lite/logs.py
CHANGED
@@ -97,13 +97,134 @@ class StandardLogFormatter(logging.Formatter):
|
|
97
97
|
##
|
98
98
|
|
99
99
|
|
100
|
+
class ProxyLogFilterer(logging.Filterer):
|
101
|
+
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
102
|
+
self._underlying = underlying
|
103
|
+
|
104
|
+
@property
|
105
|
+
def underlying(self) -> logging.Filterer:
|
106
|
+
return self._underlying
|
107
|
+
|
108
|
+
@property
|
109
|
+
def filters(self):
|
110
|
+
return self._underlying.filters
|
111
|
+
|
112
|
+
@filters.setter
|
113
|
+
def filters(self, filters):
|
114
|
+
self._underlying.filters = filters
|
115
|
+
|
116
|
+
def addFilter(self, filter): # noqa
|
117
|
+
self._underlying.addFilter(filter)
|
118
|
+
|
119
|
+
def removeFilter(self, filter): # noqa
|
120
|
+
self._underlying.removeFilter(filter)
|
121
|
+
|
122
|
+
def filter(self, record):
|
123
|
+
return self._underlying.filter(record)
|
124
|
+
|
125
|
+
|
126
|
+
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
127
|
+
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
128
|
+
ProxyLogFilterer.__init__(self, underlying)
|
129
|
+
|
130
|
+
_underlying: logging.Handler
|
131
|
+
|
132
|
+
@property
|
133
|
+
def underlying(self) -> logging.Handler:
|
134
|
+
return self._underlying
|
135
|
+
|
136
|
+
def get_name(self):
|
137
|
+
return self._underlying.get_name()
|
138
|
+
|
139
|
+
def set_name(self, name):
|
140
|
+
self._underlying.set_name(name)
|
141
|
+
|
142
|
+
@property
|
143
|
+
def name(self):
|
144
|
+
return self._underlying.name
|
145
|
+
|
146
|
+
@property
|
147
|
+
def level(self):
|
148
|
+
return self._underlying.level
|
149
|
+
|
150
|
+
@level.setter
|
151
|
+
def level(self, level):
|
152
|
+
self._underlying.level = level
|
153
|
+
|
154
|
+
@property
|
155
|
+
def formatter(self):
|
156
|
+
return self._underlying.formatter
|
157
|
+
|
158
|
+
@formatter.setter
|
159
|
+
def formatter(self, formatter):
|
160
|
+
self._underlying.formatter = formatter
|
161
|
+
|
162
|
+
def createLock(self):
|
163
|
+
self._underlying.createLock()
|
164
|
+
|
165
|
+
def acquire(self):
|
166
|
+
self._underlying.acquire()
|
167
|
+
|
168
|
+
def release(self):
|
169
|
+
self._underlying.release()
|
170
|
+
|
171
|
+
def setLevel(self, level):
|
172
|
+
self._underlying.setLevel(level)
|
173
|
+
|
174
|
+
def format(self, record):
|
175
|
+
return self._underlying.format(record)
|
176
|
+
|
177
|
+
def emit(self, record):
|
178
|
+
self._underlying.emit(record)
|
179
|
+
|
180
|
+
def handle(self, record):
|
181
|
+
return self._underlying.handle(record)
|
182
|
+
|
183
|
+
def setFormatter(self, fmt):
|
184
|
+
self._underlying.setFormatter(fmt)
|
185
|
+
|
186
|
+
def flush(self):
|
187
|
+
self._underlying.flush()
|
188
|
+
|
189
|
+
def close(self):
|
190
|
+
self._underlying.close()
|
191
|
+
|
192
|
+
def handleError(self, record):
|
193
|
+
self._underlying.handleError(record)
|
194
|
+
|
195
|
+
|
196
|
+
##
|
197
|
+
|
198
|
+
|
199
|
+
class StandardLogHandler(ProxyLogHandler):
|
200
|
+
pass
|
201
|
+
|
202
|
+
|
203
|
+
##
|
204
|
+
|
205
|
+
|
100
206
|
def configure_standard_logging(
|
101
207
|
level: ta.Union[int, str] = logging.INFO,
|
102
208
|
*,
|
103
209
|
json: bool = False,
|
104
|
-
|
210
|
+
target: ta.Optional[logging.Logger] = None,
|
211
|
+
no_check: bool = False,
|
212
|
+
) -> ta.Optional[StandardLogHandler]:
|
213
|
+
if target is None:
|
214
|
+
target = logging.root
|
215
|
+
|
216
|
+
#
|
217
|
+
|
218
|
+
if not no_check:
|
219
|
+
if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
|
220
|
+
return None
|
221
|
+
|
222
|
+
#
|
223
|
+
|
105
224
|
handler = logging.StreamHandler()
|
106
225
|
|
226
|
+
#
|
227
|
+
|
107
228
|
formatter: logging.Formatter
|
108
229
|
if json:
|
109
230
|
formatter = JsonLogFormatter()
|
@@ -111,11 +232,19 @@ def configure_standard_logging(
|
|
111
232
|
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
112
233
|
handler.setFormatter(formatter)
|
113
234
|
|
235
|
+
#
|
236
|
+
|
114
237
|
handler.addFilter(TidLogFilter())
|
115
238
|
|
116
|
-
|
239
|
+
#
|
240
|
+
|
241
|
+
target.addHandler(handler)
|
242
|
+
|
243
|
+
#
|
117
244
|
|
118
245
|
if level is not None:
|
119
|
-
|
246
|
+
target.setLevel(level)
|
247
|
+
|
248
|
+
#
|
120
249
|
|
121
|
-
return handler
|
250
|
+
return StandardLogHandler(handler)
|
omlish/logs/__init__.py
CHANGED
@@ -4,8 +4,6 @@ from .configs import ( # noqa
|
|
4
4
|
|
5
5
|
from .formatters import ( # noqa
|
6
6
|
ColorLogFormatter,
|
7
|
-
JsonLogFormatter,
|
8
|
-
StandardLogFormatter,
|
9
7
|
)
|
10
8
|
|
11
9
|
from .handlers import ( # noqa
|
@@ -15,3 +13,20 @@ from .handlers import ( # noqa
|
|
15
13
|
from .utils import ( # noqa
|
16
14
|
error_logging,
|
17
15
|
)
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
from ..lite.logs import ( # noqa
|
22
|
+
TidLogFilter,
|
23
|
+
JsonLogFormatter,
|
24
|
+
|
25
|
+
STANDARD_LOG_FORMAT_PARTS,
|
26
|
+
StandardLogFormatter,
|
27
|
+
|
28
|
+
ProxyLogFilterer,
|
29
|
+
ProxyLogHandler,
|
30
|
+
|
31
|
+
StandardLogHandler,
|
32
|
+
)
|
omlish/logs/configs.py
CHANGED
@@ -2,6 +2,7 @@ import dataclasses as dc
|
|
2
2
|
import logging
|
3
3
|
import typing as ta
|
4
4
|
|
5
|
+
from ..lite.logs import StandardLogHandler
|
5
6
|
from ..lite.logs import configure_standard_logging as configure_lite_standard_logging
|
6
7
|
from .noisy import silence_noisy_loggers
|
7
8
|
|
@@ -34,12 +35,23 @@ def configure_standard_logging(
|
|
34
35
|
level: ta.Any = None,
|
35
36
|
*,
|
36
37
|
json: bool = False,
|
37
|
-
|
38
|
+
target: logging.Logger | None = None,
|
39
|
+
no_check: bool = False,
|
40
|
+
) -> StandardLogHandler | None:
|
38
41
|
handler = configure_lite_standard_logging(
|
39
42
|
level,
|
40
43
|
json=json,
|
44
|
+
target=target,
|
45
|
+
no_check=no_check,
|
41
46
|
)
|
42
47
|
|
48
|
+
if handler is None:
|
49
|
+
return None
|
50
|
+
|
51
|
+
#
|
52
|
+
|
43
53
|
silence_noisy_loggers()
|
44
54
|
|
55
|
+
#
|
56
|
+
|
45
57
|
return handler
|
omlish/logs/formatters.py
CHANGED
omlish/marshal/__init__.py
CHANGED
@@ -49,7 +49,9 @@ from .global_ import ( # noqa
|
|
49
49
|
)
|
50
50
|
|
51
51
|
from .helpers import ( # noqa
|
52
|
+
update_field_metadata,
|
52
53
|
update_fields_metadata,
|
54
|
+
update_object_metadata,
|
53
55
|
)
|
54
56
|
|
55
57
|
from .objects import ( # noqa
|
@@ -65,6 +67,10 @@ from .polymorphism import ( # noqa
|
|
65
67
|
polymorphism_from_subclasses,
|
66
68
|
)
|
67
69
|
|
70
|
+
from .primitives import ( # noqa
|
71
|
+
PRIMITIVE_TYPES,
|
72
|
+
)
|
73
|
+
|
68
74
|
from .registries import ( # noqa
|
69
75
|
Registry,
|
70
76
|
)
|
omlish/marshal/base64.py
CHANGED
omlish/marshal/helpers.py
CHANGED
@@ -2,11 +2,24 @@ import typing as ta
|
|
2
2
|
|
3
3
|
from .. import dataclasses as dc
|
4
4
|
from .objects import FieldMetadata
|
5
|
+
from .objects import ObjectMetadata
|
5
6
|
|
6
7
|
|
7
8
|
T = ta.TypeVar('T')
|
8
9
|
|
9
10
|
|
11
|
+
def update_field_metadata(**kwargs: ta.Any) -> dc.field_modifier:
|
12
|
+
@dc.field_modifier
|
13
|
+
def inner(f: dc.Field) -> dc.Field:
|
14
|
+
return dc.update_field_metadata(f, {
|
15
|
+
FieldMetadata: dc.replace(
|
16
|
+
f.metadata.get(FieldMetadata, FieldMetadata()),
|
17
|
+
**kwargs,
|
18
|
+
),
|
19
|
+
})
|
20
|
+
return inner
|
21
|
+
|
22
|
+
|
10
23
|
def update_fields_metadata(
|
11
24
|
fields: ta.Iterable[str] | None = None,
|
12
25
|
**kwargs: ta.Any,
|
@@ -20,3 +33,17 @@ def update_fields_metadata(
|
|
20
33
|
})
|
21
34
|
|
22
35
|
return dc.update_fields(inner, fields)
|
36
|
+
|
37
|
+
|
38
|
+
def update_object_metadata(
|
39
|
+
cls: type | None = None,
|
40
|
+
**kwargs: ta.Any,
|
41
|
+
):
|
42
|
+
def inner(cls):
|
43
|
+
return dc.update_class_metadata(cls, ObjectMetadata(**kwargs))
|
44
|
+
|
45
|
+
if cls is not None:
|
46
|
+
inner(cls)
|
47
|
+
return cls
|
48
|
+
else:
|
49
|
+
return inner
|
omlish/marshal/primitives.py
CHANGED
@@ -9,6 +9,9 @@ from .base import Unmarshaler
|
|
9
9
|
from .values import Value
|
10
10
|
|
11
11
|
|
12
|
+
##
|
13
|
+
|
14
|
+
|
12
15
|
PRIMITIVE_TYPES: tuple[type, ...] = (
|
13
16
|
bool,
|
14
17
|
int,
|
@@ -19,6 +22,9 @@ PRIMITIVE_TYPES: tuple[type, ...] = (
|
|
19
22
|
)
|
20
23
|
|
21
24
|
|
25
|
+
##
|
26
|
+
|
27
|
+
|
22
28
|
class PrimitiveMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
23
29
|
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
24
30
|
if isinstance(o, PRIMITIVE_TYPES):
|
omlish/marshal/standard.py
CHANGED
@@ -27,6 +27,8 @@ from .optionals import OptionalMarshalerFactory
|
|
27
27
|
from .optionals import OptionalUnmarshalerFactory
|
28
28
|
from .primitives import PRIMITIVE_MARSHALER_FACTORY
|
29
29
|
from .primitives import PRIMITIVE_UNMARSHALER_FACTORY
|
30
|
+
from .unions import PrimitiveUnionMarshalerFactory
|
31
|
+
from .unions import PrimitiveUnionUnmarshalerFactory
|
30
32
|
from .uuids import UUID_MARSHALER_FACTORY
|
31
33
|
from .uuids import UUID_UNMARSHALER_FACTORY
|
32
34
|
|
@@ -37,6 +39,7 @@ from .uuids import UUID_UNMARSHALER_FACTORY
|
|
37
39
|
STANDARD_MARSHALER_FACTORIES: list[MarshalerFactory] = [
|
38
40
|
PRIMITIVE_MARSHALER_FACTORY,
|
39
41
|
OptionalMarshalerFactory(),
|
42
|
+
PrimitiveUnionMarshalerFactory(),
|
40
43
|
DataclassMarshalerFactory(),
|
41
44
|
EnumMarshalerFactory(),
|
42
45
|
NUMBERS_MARSHALER_FACTORY,
|
@@ -66,6 +69,7 @@ def new_standard_marshaler_factory() -> MarshalerFactory:
|
|
66
69
|
STANDARD_UNMARSHALER_FACTORIES: list[UnmarshalerFactory] = [
|
67
70
|
PRIMITIVE_UNMARSHALER_FACTORY,
|
68
71
|
OptionalUnmarshalerFactory(),
|
72
|
+
PrimitiveUnionUnmarshalerFactory(),
|
69
73
|
DataclassUnmarshalerFactory(),
|
70
74
|
EnumUnmarshalerFactory(),
|
71
75
|
NUMBERS_UNMARSHALER_FACTORY,
|
omlish/marshal/unions.py
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .. import check
|
5
|
+
from .. import matchfns as mfs
|
6
|
+
from .. import reflect as rfl
|
7
|
+
from .base import MarshalContext
|
8
|
+
from .base import Marshaler
|
9
|
+
from .base import MarshalerFactory
|
10
|
+
from .base import UnmarshalContext
|
11
|
+
from .base import Unmarshaler
|
12
|
+
from .base import UnmarshalerFactory
|
13
|
+
from .values import Value
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
class MatchUnionMarshaler(Marshaler):
|
20
|
+
mmf: mfs.MultiMatchFn[[UnmarshalContext, Value], ta.Any]
|
21
|
+
|
22
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
23
|
+
try:
|
24
|
+
m = self.mmf.match(ctx, o)
|
25
|
+
except mfs.AmbiguousMatchesError:
|
26
|
+
raise ValueError(o) # noqa
|
27
|
+
return m.fn(ctx, o)
|
28
|
+
|
29
|
+
|
30
|
+
class MatchUnionUnmarshaler(Unmarshaler):
|
31
|
+
mmf: mfs.MultiMatchFn[[UnmarshalContext, Value], ta.Any]
|
32
|
+
|
33
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
34
|
+
try:
|
35
|
+
m = self.mmf.match(ctx, v)
|
36
|
+
except mfs.AmbiguousMatchesError:
|
37
|
+
raise ValueError(v) # noqa
|
38
|
+
return m.fn(ctx, v)
|
39
|
+
|
40
|
+
|
41
|
+
##
|
42
|
+
|
43
|
+
|
44
|
+
PRIMITIVE_UNION_TYPES: tuple[type, ...] = (
|
45
|
+
float,
|
46
|
+
int,
|
47
|
+
str,
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
#
|
52
|
+
|
53
|
+
|
54
|
+
@dc.dataclass(frozen=True)
|
55
|
+
class PrimitiveUnionMarshaler(Marshaler):
|
56
|
+
tys: ta.Sequence[type]
|
57
|
+
|
58
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
59
|
+
raise NotImplementedError
|
60
|
+
|
61
|
+
|
62
|
+
@dc.dataclass(frozen=True)
|
63
|
+
class PrimitiveUnionMarshalerFactory(MarshalerFactory):
|
64
|
+
tys: ta.Sequence[type] = PRIMITIVE_UNION_TYPES
|
65
|
+
|
66
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
67
|
+
return isinstance(rty, rfl.Union) and all(a in self.tys for a in rty.args)
|
68
|
+
|
69
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
70
|
+
args = check.isinstance(rty, rfl.Union).args
|
71
|
+
return PrimitiveUnionMarshaler([t for t in self.tys if t in args])
|
72
|
+
|
73
|
+
|
74
|
+
#
|
75
|
+
|
76
|
+
|
77
|
+
@dc.dataclass(frozen=True)
|
78
|
+
class PrimitiveUnionUnmarshaler(Unmarshaler):
|
79
|
+
tys: ta.Sequence[type]
|
80
|
+
|
81
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
82
|
+
raise NotImplementedError
|
83
|
+
|
84
|
+
|
85
|
+
@dc.dataclass(frozen=True)
|
86
|
+
class PrimitiveUnionUnmarshalerFactory(UnmarshalerFactory):
|
87
|
+
tys: ta.Sequence[type] = PRIMITIVE_UNION_TYPES
|
88
|
+
|
89
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
90
|
+
return isinstance(rty, rfl.Union) and all(a in self.tys for a in rty.args)
|
91
|
+
|
92
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
93
|
+
args = check.isinstance(rty, rfl.Union).args
|
94
|
+
return PrimitiveUnionUnmarshaler([t for t in self.tys if t in args])
|
95
|
+
|
96
|
+
|
97
|
+
#
|
98
|
+
|
99
|
+
|
100
|
+
PRIMITIVE_UNION_MARSHALER_FACTORY = PrimitiveUnionMarshalerFactory()
|
101
|
+
PRIMITIVE_UNION_UNMARSHALER_FACTORY = PrimitiveUnionUnmarshalerFactory()
|
omlish/matchfns.py
CHANGED
@@ -93,7 +93,7 @@ class MultiMatchFn(MatchFn[P, T]):
|
|
93
93
|
children: ta.Sequence[MatchFn[P, T]]
|
94
94
|
strict: bool = False
|
95
95
|
|
96
|
-
def
|
96
|
+
def match(self, *args: P.args, **kwargs: P.kwargs) -> MatchFn[P, T] | None:
|
97
97
|
matches = []
|
98
98
|
for cur in self.children:
|
99
99
|
if cur.guard(*args, **kwargs):
|
@@ -109,10 +109,10 @@ class MultiMatchFn(MatchFn[P, T]):
|
|
109
109
|
return matches[0]
|
110
110
|
|
111
111
|
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
112
|
-
return self.
|
112
|
+
return self.match(*args, **kwargs) is not None
|
113
113
|
|
114
114
|
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
115
|
-
if (m := self.
|
115
|
+
if (m := self.match(*args, **kwargs)) is None:
|
116
116
|
raise MatchGuardError(*args, **kwargs)
|
117
117
|
return m.fn(*args, **kwargs)
|
118
118
|
|
@@ -37,12 +37,12 @@ class Keywords(lang.Final):
|
|
37
37
|
@cached.property
|
38
38
|
@dc.init
|
39
39
|
def by_type(self) -> ta.Mapping[type[Keyword], Keyword]:
|
40
|
-
return col.
|
40
|
+
return col.make_map_by(type, self.lst, strict=True) # noqa
|
41
41
|
|
42
42
|
@cached.property
|
43
43
|
@dc.init
|
44
44
|
def by_tag(self) -> ta.Mapping[str, Keyword]:
|
45
|
-
return col.
|
45
|
+
return col.make_map_by(operator.attrgetter('tag'), self.lst, strict=True) # noqa
|
46
46
|
|
47
47
|
def __getitem__(self, item: type[KeywordT] | str) -> KeywordT:
|
48
48
|
if isinstance(item, type):
|
@@ -24,7 +24,7 @@ KeywordT = ta.TypeVar('KeywordT', bound=Keyword)
|
|
24
24
|
##
|
25
25
|
|
26
26
|
|
27
|
-
KEYWORD_TYPES_BY_TAG: ta.Mapping[str, type[Keyword]] = col.
|
27
|
+
KEYWORD_TYPES_BY_TAG: ta.Mapping[str, type[Keyword]] = col.make_map_by( # noqa
|
28
28
|
operator.attrgetter('tag'),
|
29
29
|
(cls for cls in lang.deep_subclasses(Keyword) if not lang.is_abstract_class(cls)),
|
30
30
|
strict=True,
|
omlish/sql/__init__.py
CHANGED
@@ -7,3 +7,21 @@ from .asyncs import ( # noqa
|
|
7
7
|
AsyncTransactionLike,
|
8
8
|
async_adapt,
|
9
9
|
)
|
10
|
+
|
11
|
+
from .dbs import ( # noqa
|
12
|
+
DbLoc,
|
13
|
+
DbSpec,
|
14
|
+
DbType,
|
15
|
+
DbTypes,
|
16
|
+
HostDbLoc,
|
17
|
+
UrlDbLoc,
|
18
|
+
)
|
19
|
+
|
20
|
+
from .exprs import ( # noqa
|
21
|
+
paren,
|
22
|
+
)
|
23
|
+
|
24
|
+
from .qualifiedname import ( # noqa
|
25
|
+
QualifiedName,
|
26
|
+
qn,
|
27
|
+
)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import collections.abc
|
2
|
+
import dataclasses as dc
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
@dc.dataclass(frozen=True)
|
7
|
+
class QualifiedName(ta.Sequence[str]):
|
8
|
+
parts: ta.Sequence[str]
|
9
|
+
|
10
|
+
def __post_init__(self) -> None:
|
11
|
+
if not (
|
12
|
+
self.parts and
|
13
|
+
not isinstance(self.parts, str) and
|
14
|
+
all(self.parts) and
|
15
|
+
all(isinstance(p, str) for p in self.parts)
|
16
|
+
):
|
17
|
+
raise ValueError(self)
|
18
|
+
|
19
|
+
def __repr__(self) -> str:
|
20
|
+
return f'{self.__class__.__name__}([{", ".join(map(repr, self.parts))}])'
|
21
|
+
|
22
|
+
@property
|
23
|
+
def dotted(self) -> str:
|
24
|
+
return '.'.join(self.parts)
|
25
|
+
|
26
|
+
def prefixed(self, sz: int) -> tuple[str | None, ...]:
|
27
|
+
if len(self) > sz:
|
28
|
+
raise ValueError(self)
|
29
|
+
return ((None,) * (sz - len(self))) + tuple(self.parts)
|
30
|
+
|
31
|
+
@property
|
32
|
+
def pair(self) -> tuple[str | None, str]:
|
33
|
+
return self.prefixed(2) # type: ignore
|
34
|
+
|
35
|
+
@property
|
36
|
+
def triple(self) -> tuple[str | None, str | None, str]:
|
37
|
+
return self.prefixed(3) # type: ignore
|
38
|
+
|
39
|
+
@property
|
40
|
+
def quad(self) -> tuple[str | None, str | None, str | None, str]:
|
41
|
+
return self.prefixed(4) # type: ignore
|
42
|
+
|
43
|
+
def __iter__(self) -> ta.Iterator[str]:
|
44
|
+
return iter(self.parts)
|
45
|
+
|
46
|
+
def __len__(self) -> int:
|
47
|
+
return len(self.parts)
|
48
|
+
|
49
|
+
def __getitem__(self, idx: int) -> str: # type: ignore
|
50
|
+
return self.parts[idx]
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def of_dotted(cls, dotted: str) -> 'QualifiedName':
|
54
|
+
return cls(dotted.split('.'))
|
55
|
+
|
56
|
+
@classmethod
|
57
|
+
def of(
|
58
|
+
cls,
|
59
|
+
obj: ta.Union['QualifiedName', ta.Iterable[str]],
|
60
|
+
) -> 'QualifiedName':
|
61
|
+
if isinstance(obj, QualifiedName):
|
62
|
+
return obj
|
63
|
+
elif isinstance(obj, str):
|
64
|
+
raise TypeError(obj)
|
65
|
+
elif isinstance(obj, collections.abc.Iterable):
|
66
|
+
return cls(list(obj))
|
67
|
+
else:
|
68
|
+
raise TypeError(obj)
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
def of_optional(
|
72
|
+
cls,
|
73
|
+
obj: ta.Union['QualifiedName', ta.Iterable[str], None],
|
74
|
+
) -> ta.Optional['QualifiedName']:
|
75
|
+
if obj is None:
|
76
|
+
return None
|
77
|
+
else:
|
78
|
+
return cls.of(obj)
|
79
|
+
|
80
|
+
|
81
|
+
def qn(*args: str) -> QualifiedName:
|
82
|
+
return QualifiedName(args)
|
omlish/stats.py
CHANGED