omlish 0.0.0.dev133__py3-none-any.whl → 0.0.0.dev177__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.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +5 -3
  3. omlish/antlr/_runtime/__init__.py +0 -22
  4. omlish/antlr/_runtime/_all.py +24 -0
  5. omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
  6. omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
  7. omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
  8. omlish/antlr/_runtime/xpath/XPath.py +7 -1
  9. omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
  10. omlish/antlr/delimit.py +106 -0
  11. omlish/antlr/dot.py +31 -0
  12. omlish/antlr/errors.py +11 -0
  13. omlish/antlr/input.py +96 -0
  14. omlish/antlr/parsing.py +19 -0
  15. omlish/antlr/runtime.py +102 -0
  16. omlish/antlr/utils.py +38 -0
  17. omlish/argparse/all.py +45 -0
  18. omlish/{argparse.py → argparse/cli.py} +112 -107
  19. omlish/asyncs/__init__.py +0 -35
  20. omlish/asyncs/all.py +35 -0
  21. omlish/asyncs/asyncio/all.py +7 -0
  22. omlish/asyncs/asyncio/channels.py +40 -0
  23. omlish/asyncs/asyncio/streams.py +45 -0
  24. omlish/asyncs/asyncio/subprocesses.py +238 -0
  25. omlish/asyncs/asyncio/timeouts.py +16 -0
  26. omlish/asyncs/bluelet/LICENSE +6 -0
  27. omlish/asyncs/bluelet/all.py +67 -0
  28. omlish/asyncs/bluelet/api.py +23 -0
  29. omlish/asyncs/bluelet/core.py +178 -0
  30. omlish/asyncs/bluelet/events.py +78 -0
  31. omlish/asyncs/bluelet/files.py +80 -0
  32. omlish/asyncs/bluelet/runner.py +416 -0
  33. omlish/asyncs/bluelet/sockets.py +214 -0
  34. omlish/bootstrap/sys.py +3 -3
  35. omlish/cached.py +2 -2
  36. omlish/check.py +49 -460
  37. omlish/codecs/__init__.py +72 -0
  38. omlish/codecs/base.py +106 -0
  39. omlish/codecs/bytes.py +119 -0
  40. omlish/codecs/chain.py +23 -0
  41. omlish/codecs/funcs.py +39 -0
  42. omlish/codecs/registry.py +139 -0
  43. omlish/codecs/standard.py +4 -0
  44. omlish/codecs/text.py +217 -0
  45. omlish/collections/cache/impl.py +50 -57
  46. omlish/collections/coerce.py +1 -0
  47. omlish/collections/mappings.py +1 -1
  48. omlish/configs/flattening.py +1 -1
  49. omlish/defs.py +1 -1
  50. omlish/diag/_pycharm/runhack.py +8 -2
  51. omlish/diag/procfs.py +8 -8
  52. omlish/docker/__init__.py +0 -36
  53. omlish/docker/all.py +31 -0
  54. omlish/docker/consts.py +4 -0
  55. omlish/{lite/docker.py → docker/detect.py} +18 -0
  56. omlish/docker/{helpers.py → timebomb.py} +0 -21
  57. omlish/formats/cbor.py +31 -0
  58. omlish/formats/cloudpickle.py +31 -0
  59. omlish/formats/codecs.py +93 -0
  60. omlish/formats/json/codecs.py +29 -0
  61. omlish/formats/json/delimted.py +4 -0
  62. omlish/formats/json/stream/errors.py +2 -0
  63. omlish/formats/json/stream/lex.py +12 -6
  64. omlish/formats/json/stream/parse.py +38 -22
  65. omlish/formats/json5.py +31 -0
  66. omlish/formats/pickle.py +31 -0
  67. omlish/formats/repr.py +25 -0
  68. omlish/formats/toml.py +17 -0
  69. omlish/formats/yaml.py +25 -0
  70. omlish/funcs/__init__.py +0 -0
  71. omlish/{genmachine.py → funcs/genmachine.py} +5 -4
  72. omlish/{matchfns.py → funcs/match.py} +1 -1
  73. omlish/funcs/pairs.py +215 -0
  74. omlish/http/__init__.py +0 -48
  75. omlish/http/all.py +48 -0
  76. omlish/http/coro/__init__.py +0 -0
  77. omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
  78. omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
  79. omlish/{lite/http → http}/handlers.py +3 -2
  80. omlish/{lite/http → http}/parsing.py +1 -0
  81. omlish/http/sessions.py +1 -1
  82. omlish/{lite/http → http}/versions.py +1 -0
  83. omlish/inject/managed.py +2 -2
  84. omlish/io/__init__.py +0 -3
  85. omlish/{lite/io.py → io/buffers.py} +8 -9
  86. omlish/io/compress/__init__.py +9 -0
  87. omlish/io/compress/abc.py +104 -0
  88. omlish/io/compress/adapters.py +148 -0
  89. omlish/io/compress/base.py +24 -0
  90. omlish/io/compress/brotli.py +47 -0
  91. omlish/io/compress/bz2.py +61 -0
  92. omlish/io/compress/codecs.py +78 -0
  93. omlish/io/compress/gzip.py +350 -0
  94. omlish/io/compress/lz4.py +91 -0
  95. omlish/io/compress/lzma.py +81 -0
  96. omlish/io/compress/snappy.py +34 -0
  97. omlish/io/compress/zlib.py +74 -0
  98. omlish/io/compress/zstd.py +44 -0
  99. omlish/io/fdio/__init__.py +1 -0
  100. omlish/{lite → io}/fdio/handlers.py +5 -5
  101. omlish/{lite → io}/fdio/kqueue.py +8 -8
  102. omlish/{lite → io}/fdio/manager.py +7 -7
  103. omlish/{lite → io}/fdio/pollers.py +13 -13
  104. omlish/io/generators/__init__.py +56 -0
  105. omlish/io/generators/consts.py +1 -0
  106. omlish/io/generators/direct.py +13 -0
  107. omlish/io/generators/readers.py +189 -0
  108. omlish/io/generators/stepped.py +191 -0
  109. omlish/io/pyio.py +5 -2
  110. omlish/iterators/__init__.py +24 -0
  111. omlish/iterators/iterators.py +132 -0
  112. omlish/iterators/recipes.py +18 -0
  113. omlish/iterators/tools.py +96 -0
  114. omlish/iterators/unique.py +67 -0
  115. omlish/lang/__init__.py +13 -1
  116. omlish/lang/functions.py +11 -2
  117. omlish/lang/generators.py +243 -0
  118. omlish/lang/iterables.py +46 -49
  119. omlish/lang/maybes.py +4 -4
  120. omlish/lite/cached.py +39 -6
  121. omlish/lite/check.py +438 -75
  122. omlish/lite/contextmanagers.py +17 -4
  123. omlish/lite/dataclasses.py +42 -0
  124. omlish/lite/inject.py +28 -45
  125. omlish/lite/logs.py +0 -270
  126. omlish/lite/marshal.py +309 -144
  127. omlish/lite/pycharm.py +47 -0
  128. omlish/lite/reflect.py +33 -0
  129. omlish/lite/resources.py +8 -0
  130. omlish/lite/runtime.py +4 -4
  131. omlish/lite/shlex.py +12 -0
  132. omlish/lite/socketserver.py +2 -2
  133. omlish/lite/strings.py +31 -0
  134. omlish/logs/__init__.py +0 -32
  135. omlish/logs/{_abc.py → abc.py} +0 -1
  136. omlish/logs/all.py +37 -0
  137. omlish/logs/{formatters.py → color.py} +1 -2
  138. omlish/logs/configs.py +7 -38
  139. omlish/logs/filters.py +10 -0
  140. omlish/logs/handlers.py +4 -1
  141. omlish/logs/json.py +56 -0
  142. omlish/logs/proxy.py +99 -0
  143. omlish/logs/standard.py +128 -0
  144. omlish/logs/utils.py +2 -2
  145. omlish/manifests/__init__.py +2 -0
  146. omlish/manifests/load.py +209 -0
  147. omlish/manifests/types.py +17 -0
  148. omlish/marshal/base.py +1 -1
  149. omlish/marshal/factories.py +1 -1
  150. omlish/marshal/forbidden.py +1 -1
  151. omlish/marshal/iterables.py +1 -1
  152. omlish/marshal/literals.py +50 -0
  153. omlish/marshal/mappings.py +1 -1
  154. omlish/marshal/maybes.py +1 -1
  155. omlish/marshal/standard.py +5 -1
  156. omlish/marshal/unions.py +1 -1
  157. omlish/os/__init__.py +0 -0
  158. omlish/os/atomics.py +205 -0
  159. omlish/os/deathsig.py +23 -0
  160. omlish/{os.py → os/files.py} +0 -9
  161. omlish/{lite → os}/journald.py +2 -1
  162. omlish/os/linux.py +484 -0
  163. omlish/os/paths.py +36 -0
  164. omlish/{lite → os}/pidfile.py +1 -0
  165. omlish/os/sizes.py +9 -0
  166. omlish/reflect/__init__.py +3 -0
  167. omlish/reflect/subst.py +2 -1
  168. omlish/reflect/types.py +126 -44
  169. omlish/secrets/pwhash.py +1 -1
  170. omlish/secrets/subprocesses.py +3 -1
  171. omlish/specs/jsonrpc/marshal.py +1 -1
  172. omlish/specs/openapi/marshal.py +1 -1
  173. omlish/sql/alchemy/asyncs.py +1 -1
  174. omlish/sql/queries/__init__.py +9 -1
  175. omlish/sql/queries/building.py +3 -0
  176. omlish/sql/queries/exprs.py +10 -27
  177. omlish/sql/queries/idents.py +48 -10
  178. omlish/sql/queries/names.py +80 -13
  179. omlish/sql/queries/params.py +64 -0
  180. omlish/sql/queries/rendering.py +1 -1
  181. omlish/subprocesses.py +340 -0
  182. omlish/term.py +29 -14
  183. omlish/testing/pytest/marks.py +2 -2
  184. omlish/testing/pytest/plugins/asyncs.py +6 -1
  185. omlish/testing/pytest/plugins/logging.py +1 -1
  186. omlish/testing/pytest/plugins/switches.py +1 -1
  187. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
  188. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
  189. omlish/fnpairs.py +0 -496
  190. omlish/formats/json/cli/__main__.py +0 -11
  191. omlish/formats/json/cli/cli.py +0 -298
  192. omlish/formats/json/cli/formats.py +0 -71
  193. omlish/formats/json/cli/io.py +0 -74
  194. omlish/formats/json/cli/parsing.py +0 -82
  195. omlish/formats/json/cli/processing.py +0 -48
  196. omlish/formats/json/cli/rendering.py +0 -92
  197. omlish/iterators.py +0 -300
  198. omlish/lite/subprocesses.py +0 -130
  199. /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
  200. /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
  201. /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
  202. /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
  203. /omlish/collections/{_abc.py → abc.py} +0 -0
  204. /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
  205. /omlish/io/{_abc.py → abc.py} +0 -0
  206. /omlish/sql/{_abc.py → abc.py} +0 -0
  207. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/top_level.txt +0 -0
omlish/reflect/types.py CHANGED
@@ -7,6 +7,7 @@ TODO:
7
7
  - cache this shit, esp generic_mro shit
8
8
  - cache __hash__ in Generic/Union
9
9
  """
10
+ import abc
10
11
  import dataclasses as dc
11
12
  import types
12
13
  import typing as ta
@@ -17,11 +18,12 @@ _NoneType = types.NoneType # type: ignore
17
18
  _NONE_TYPE_FROZENSET: frozenset['Type'] = frozenset([_NoneType])
18
19
 
19
20
 
20
- _GenericAlias = ta._GenericAlias # type: ignore # noqa
21
+ _AnnotatedAlias = ta._AnnotatedAlias # type: ignore # noqa
21
22
  _CallableGenericAlias = ta._CallableGenericAlias # type: ignore # noqa
23
+ _GenericAlias = ta._GenericAlias # type: ignore # noqa
24
+ _LiteralGenericAlias = ta._LiteralGenericAlias # type: ignore # noqa
22
25
  _SpecialGenericAlias = ta._SpecialGenericAlias # type: ignore # noqa
23
26
  _UnionGenericAlias = ta._UnionGenericAlias # type: ignore # noqa
24
- _AnnotatedAlias = ta._AnnotatedAlias # type: ignore # noqa
25
27
 
26
28
 
27
29
  ##
@@ -116,19 +118,29 @@ def get_newtype_supertype(obj: ta.Any) -> ta.Any:
116
118
  ##
117
119
 
118
120
 
119
- Type: ta.TypeAlias = ta.Union[
121
+ class TypeInfo(abc.ABC): # noqa
122
+ pass
123
+
124
+
125
+ Type: ta.TypeAlias = ta.Union[ # noqa
120
126
  type,
121
127
  ta.TypeVar,
122
- 'Union',
123
- 'Generic',
124
- 'NewType',
125
- 'Annotated',
126
- 'Any',
128
+ TypeInfo,
127
129
  ]
128
130
 
129
131
 
132
+ TYPES: tuple[type, ...] = (
133
+ type,
134
+ ta.TypeVar,
135
+ TypeInfo,
136
+ )
137
+
138
+
139
+ ##
140
+
141
+
130
142
  @dc.dataclass(frozen=True)
131
- class Union:
143
+ class Union(TypeInfo):
132
144
  args: frozenset[Type]
133
145
 
134
146
  @property
@@ -144,8 +156,11 @@ class Union:
144
156
  return Union(rem)
145
157
 
146
158
 
159
+ #
160
+
161
+
147
162
  @dc.dataclass(frozen=True)
148
- class Generic:
163
+ class Generic(TypeInfo):
149
164
  cls: type
150
165
  args: tuple[Type, ...] # map[int, V] = (int, V) | map[T, T] = (T, T)
151
166
 
@@ -167,39 +182,51 @@ class Generic:
167
182
  )
168
183
 
169
184
 
185
+ #
186
+
187
+
170
188
  @dc.dataclass(frozen=True)
171
- class NewType:
189
+ class NewType(TypeInfo):
172
190
  obj: ta.Any
173
191
  ty: Type
174
192
 
175
193
 
194
+ #
195
+
196
+
176
197
  @dc.dataclass(frozen=True)
177
- class Annotated:
198
+ class Annotated(TypeInfo):
178
199
  ty: Type
179
200
  md: ta.Sequence[ta.Any]
180
201
 
181
202
  obj: ta.Any = dc.field(compare=False, repr=False)
182
203
 
183
204
 
184
- class Any:
205
+ #
206
+
207
+
208
+ @dc.dataclass(frozen=True)
209
+ class Literal(TypeInfo):
210
+ args: tuple[ta.Any, ...]
211
+
212
+ obj: ta.Any = dc.field(compare=False, repr=False)
213
+
214
+
215
+ #
216
+
217
+
218
+ class Any(TypeInfo):
185
219
  pass
186
220
 
187
221
 
188
222
  ANY = Any()
189
223
 
190
224
 
191
- TYPES: tuple[type, ...] = (
192
- type,
193
- ta.TypeVar,
194
- Union,
195
- Generic,
196
- NewType,
197
- Annotated,
198
- Any,
199
- )
225
+ ##
200
226
 
201
227
 
202
- ##
228
+ class ReflectTypeError(TypeError):
229
+ pass
203
230
 
204
231
 
205
232
  class Reflector:
@@ -212,57 +239,80 @@ class Reflector:
212
239
 
213
240
  self._override = override
214
241
 
242
+ #
243
+
215
244
  def is_type(self, obj: ta.Any) -> bool:
216
- if isinstance(obj, (Union, Generic, ta.TypeVar, NewType, Any)): # noqa
245
+ try:
246
+ self._type(obj, check_only=True)
247
+ except ReflectTypeError:
248
+ return False
249
+ else:
217
250
  return True
218
251
 
219
- oty = type(obj)
220
-
221
- return (
222
- oty is _UnionGenericAlias or oty is types.UnionType or # noqa
223
-
224
- isinstance(obj, ta.NewType) or # noqa
225
-
226
- (
227
- is_simple_generic_alias_type(oty) or
228
- oty is _CallableGenericAlias
229
- ) or
252
+ def type(self, obj: ta.Any) -> Type:
253
+ if (ty := self._type(obj, check_only=False)) is None:
254
+ raise RuntimeError(obj)
255
+ return ty
230
256
 
231
- isinstance(obj, type) or
257
+ #
232
258
 
233
- isinstance(obj, _SpecialGenericAlias)
234
- )
259
+ def _type(self, obj: ta.Any, *, check_only: bool) -> Type | None:
260
+ ##
261
+ # Overrides beat everything
235
262
 
236
- def type(self, obj: ta.Any) -> Type:
237
263
  if self._override is not None:
238
264
  if (ovr := self._override(obj)) is not None:
239
265
  return ovr
240
266
 
267
+ ##
268
+ # Any
269
+
241
270
  if obj is ta.Any:
242
271
  return ANY
243
272
 
244
- if isinstance(obj, (Union, Generic, ta.TypeVar, NewType, Any)): # noqa
273
+ ##
274
+ # Already a Type?
275
+
276
+ if isinstance(obj, (ta.TypeVar, TypeInfo)): # noqa
245
277
  return obj
246
278
 
247
279
  oty = type(obj)
248
280
 
281
+ ##
282
+ # Union
283
+
249
284
  if oty is _UnionGenericAlias or oty is types.UnionType:
285
+ if check_only:
286
+ return None
287
+
250
288
  return Union(frozenset(self.type(a) for a in ta.get_args(obj)))
251
289
 
290
+ ##
291
+ # NewType
292
+
252
293
  if isinstance(obj, ta.NewType): # noqa
294
+ if check_only:
295
+ return None
296
+
253
297
  return NewType(obj, get_newtype_supertype(obj))
254
298
 
299
+ ##
300
+ # Simple Generic
301
+
255
302
  if (
256
303
  is_simple_generic_alias_type(oty) or
257
304
  oty is _CallableGenericAlias
258
305
  ):
306
+ if check_only:
307
+ return None
308
+
259
309
  origin = ta.get_origin(obj)
260
310
  args = ta.get_args(obj)
261
311
 
262
312
  if oty is _CallableGenericAlias:
263
313
  p, r = args
264
314
  if p is Ellipsis or isinstance(p, ta.ParamSpec):
265
- raise TypeError(f'Callable argument not yet supported for {obj=}')
315
+ raise ReflectTypeError(f'Callable argument not yet supported for {obj=}')
266
316
  args = (*p, r)
267
317
  params = _KNOWN_SPECIAL_TYPE_VARS[:len(args)]
268
318
 
@@ -276,7 +326,7 @@ class Reflector:
276
326
  params = get_params(origin)
277
327
 
278
328
  if len(args) != len(params):
279
- raise TypeError(f'Mismatched {args=} and {params=} for {obj=}')
329
+ raise ReflectTypeError(f'Mismatched {args=} and {params=} for {obj=}')
280
330
 
281
331
  return Generic(
282
332
  origin,
@@ -285,7 +335,13 @@ class Reflector:
285
335
  obj,
286
336
  )
287
337
 
338
+ ##
339
+ # Full Generic
340
+
288
341
  if isinstance(obj, type):
342
+ if check_only:
343
+ return None
344
+
289
345
  if issubclass(obj, ta.Generic): # type: ignore
290
346
  params = get_params(obj)
291
347
  if params:
@@ -295,12 +351,20 @@ class Reflector:
295
351
  params,
296
352
  obj,
297
353
  )
354
+
298
355
  return obj
299
356
 
357
+ ##
358
+ # Special Generic
359
+
300
360
  if isinstance(obj, _SpecialGenericAlias):
301
361
  if (ks := _KNOWN_SPECIALS_BY_ALIAS.get(obj)) is not None:
362
+ if check_only:
363
+ return None
364
+
302
365
  if (np := ks.nparams) < 0:
303
- raise TypeError(obj)
366
+ raise ReflectTypeError(obj)
367
+
304
368
  params = _KNOWN_SPECIAL_TYPE_VARS[:np]
305
369
  return Generic(
306
370
  ks.origin,
@@ -309,11 +373,29 @@ class Reflector:
309
373
  obj,
310
374
  )
311
375
 
376
+ ##
377
+ # Annotated
378
+
312
379
  if isinstance(obj, _AnnotatedAlias):
380
+ if check_only:
381
+ return None
382
+
313
383
  o = ta.get_args(obj)[0]
314
384
  return Annotated(self.type(o), md=obj.__metadata__, obj=obj)
315
385
 
316
- raise TypeError(obj)
386
+ ##
387
+ # Literal
388
+
389
+ if isinstance(obj, _LiteralGenericAlias):
390
+ if check_only:
391
+ return None
392
+
393
+ return Literal(ta.get_args(obj), obj=obj)
394
+
395
+ ##
396
+ # Failure
397
+
398
+ raise ReflectTypeError(obj)
317
399
 
318
400
 
319
401
  DEFAULT_REFLECTOR = Reflector()
omlish/secrets/pwhash.py CHANGED
@@ -28,7 +28,7 @@ import secrets
28
28
 
29
29
 
30
30
  SALT_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
31
- DEFAULT_PBKDF2_ITERATIONS = 600000
31
+ DEFAULT_PBKDF2_ITERATIONS = 600_000
32
32
 
33
33
 
34
34
  def gen_salt(length: int) -> str:
@@ -32,7 +32,9 @@ def pipe_fd_subprocess_file_input(buf: bytes) -> ta.Iterator[SubprocessFileInput
32
32
  try:
33
33
  if hasattr(fcntl, 'F_SETPIPE_SZ'):
34
34
  fcntl.fcntl(wfd, fcntl.F_SETPIPE_SZ, max(len(buf), 0x1000))
35
- os.write(wfd, buf)
35
+ n = os.write(wfd, buf)
36
+ if n != len(buf):
37
+ raise OSError(f'Failed to write data to pipe: {n=} {len(buf)=}')
36
38
  os.close(wfd)
37
39
  closed_wfd = True
38
40
  yield SubprocessFileInput(f'/dev/fd/{rfd}', [rfd])
@@ -4,8 +4,8 @@ from ... import check
4
4
  from ... import dataclasses as dc
5
5
  from ... import lang
6
6
  from ... import marshal as msh
7
- from ... import matchfns as mfs
8
7
  from ... import reflect as rfl
8
+ from ...funcs import match as mfs
9
9
  from .types import NotSpecified
10
10
 
11
11
 
@@ -4,8 +4,8 @@ from ... import check
4
4
  from ... import dataclasses as dc
5
5
  from ... import lang
6
6
  from ... import marshal as msh
7
- from ... import matchfns as mfs
8
7
  from ... import reflect as rfl
8
+ from ...funcs import match as mfs
9
9
  from .openapi import Reference
10
10
 
11
11
 
@@ -9,7 +9,7 @@ import typing as ta
9
9
  import sqlalchemy as sa
10
10
  import sqlalchemy.ext.asyncio as saa
11
11
 
12
- from ... import asyncs as au
12
+ from ...asyncs import all as au
13
13
 
14
14
 
15
15
  T = ta.TypeVar('T')
@@ -22,13 +22,13 @@ from .exprs import ( # noqa
22
22
  ExprBuilder,
23
23
  Literal,
24
24
  NameExpr,
25
- Param,
26
25
  )
27
26
 
28
27
  from .idents import ( # noqa
29
28
  CanIdent,
30
29
  Ident,
31
30
  IdentBuilder,
31
+ IdentLike,
32
32
  )
33
33
 
34
34
  from .multi import ( # noqa
@@ -41,12 +41,20 @@ from .names import ( # noqa
41
41
  CanName,
42
42
  Name,
43
43
  NameBuilder,
44
+ NameLike,
44
45
  )
45
46
 
46
47
  from .ops import ( # noqa
47
48
  OpKind,
48
49
  )
49
50
 
51
+ from .params import ( # noqa
52
+ CanParam,
53
+ Param,
54
+ ParamBuilder,
55
+ as_param,
56
+ )
57
+
50
58
  from .relations import ( # noqa
51
59
  CanRelation,
52
60
  CanTable,
@@ -5,6 +5,7 @@ from .idents import IdentBuilder
5
5
  from .inserts import InsertBuilder
6
6
  from .multi import MultiBuilder
7
7
  from .names import NameBuilder
8
+ from .params import ParamBuilder
8
9
  from .relations import RelationBuilder
9
10
  from .selects import SelectBuilder
10
11
  from .stmts import StmtBuilder
@@ -23,6 +24,8 @@ class StdBuilder(
23
24
 
24
25
  RelationBuilder,
25
26
 
27
+ ParamBuilder,
28
+
26
29
  NameBuilder,
27
30
  IdentBuilder,
28
31
 
@@ -8,10 +8,11 @@ import typing as ta
8
8
  from ... import lang
9
9
  from .base import Node
10
10
  from .base import Value
11
- from .idents import Ident
11
+ from .idents import IdentLike
12
12
  from .names import CanName
13
13
  from .names import Name
14
14
  from .names import NameBuilder
15
+ from .names import NameLike
15
16
 
16
17
 
17
18
  ##
@@ -21,30 +22,21 @@ class Expr(Node, lang.Abstract):
21
22
  pass
22
23
 
23
24
 
25
+ #
26
+
27
+
24
28
  class Literal(Expr, lang.Final):
25
29
  v: Value
26
30
 
27
31
 
28
- class NameExpr(Expr, lang.Final):
29
- n: Name
32
+ #
30
33
 
31
34
 
32
- class Param(Expr, lang.Final):
33
- n: str | None = None
35
+ class NameExpr(Expr, lang.Final):
36
+ n: Name
34
37
 
35
- def __repr__(self) -> str:
36
- if self.n is not None:
37
- return f'{self.__class__.__name__}({self.n!r})'
38
- else:
39
- return f'{self.__class__.__name__}(@{hex(id(self))[2:]})'
40
38
 
41
- def __eq__(self, other):
42
- if not isinstance(other, Param):
43
- return False
44
- if self.n is None and other.n is None:
45
- return self is other
46
- else:
47
- return self.n == other.n
39
+ ##
48
40
 
49
41
 
50
42
  CanLiteral: ta.TypeAlias = Literal | Value
@@ -69,7 +61,7 @@ class ExprBuilder(NameBuilder):
69
61
  def expr(self, o: CanExpr) -> Expr:
70
62
  if isinstance(o, Expr):
71
63
  return o
72
- elif isinstance(o, (Name, Ident)):
64
+ elif isinstance(o, (NameLike, IdentLike)):
73
65
  return NameExpr(self.name(o))
74
66
  else:
75
67
  return self.literal(o)
@@ -77,12 +69,3 @@ class ExprBuilder(NameBuilder):
77
69
  @ta.final
78
70
  def e(self, o: CanExpr) -> Expr:
79
71
  return self.expr(o)
80
-
81
- #
82
-
83
- def param(self, n: str | None = None) -> Param:
84
- return Param(n)
85
-
86
- @ta.final
87
- def p(self, n: str | None = None) -> Param:
88
- return self.param(n)
@@ -1,3 +1,5 @@
1
+ import abc
2
+ import functools
1
3
  import typing as ta
2
4
 
3
5
  from ... import lang
@@ -8,22 +10,58 @@ from .base import Node
8
10
  ##
9
11
 
10
12
 
11
- class Ident(Node, lang.Final):
13
+ class IdentLike(abc.ABC): # noqa
14
+ pass
15
+
16
+
17
+ ##
18
+
19
+
20
+ class Ident(Node, IdentLike, lang.Final):
12
21
  s: str
13
22
 
14
23
 
15
- CanIdent: ta.TypeAlias = Ident | str
24
+ ##
25
+
26
+
27
+ @functools.singledispatch
28
+ def as_ident(o: ta.Any) -> Ident:
29
+ raise TypeError(o)
30
+
31
+
32
+ @as_ident.register
33
+ def _(i: Ident) -> Ident:
34
+ return i
35
+
36
+
37
+ @as_ident.register
38
+ def _(s: str) -> Ident:
39
+ return Ident(s)
40
+
41
+
42
+ ##
43
+
44
+
45
+ CanIdent: ta.TypeAlias = IdentLike | Ident | str
46
+
47
+
48
+ class IdentAccessor(lang.Final):
49
+ def __getattr__(self, s: str) -> Ident:
50
+ return Ident(s)
51
+
52
+ def __call__(self, o: CanIdent) -> Ident:
53
+ return as_ident(o)
54
+
55
+
56
+ ##
16
57
 
17
58
 
18
59
  class IdentBuilder(Builder):
60
+ @ta.final
19
61
  def ident(self, o: CanIdent) -> Ident:
20
- if isinstance(o, Ident):
21
- return o
22
- elif isinstance(o, str):
23
- return Ident(o)
24
- else:
25
- raise TypeError(o)
62
+ return as_ident(o)
26
63
 
27
64
  @ta.final
28
- def i(self, o: CanIdent) -> Ident:
29
- return self.ident(o)
65
+ @property
66
+ def i(self) -> IdentAccessor:
67
+ return IdentAccessor()
@@ -1,34 +1,101 @@
1
+ import abc
2
+ import functools
1
3
  import typing as ta
2
4
 
5
+ from ... import check
3
6
  from ... import dataclasses as dc
4
7
  from ... import lang
5
8
  from .base import Node
6
9
  from .idents import CanIdent
7
10
  from .idents import Ident
8
11
  from .idents import IdentBuilder
12
+ from .idents import as_ident
9
13
 
10
14
 
11
15
  ##
12
16
 
13
17
 
14
- class Name(Node, lang.Final):
15
- ps: ta.Sequence[Ident] = dc.xfield(coerce=tuple)
18
+ class NameLike(abc.ABC): # noqa
19
+ pass
16
20
 
17
21
 
18
- CanName: ta.TypeAlias = Name | CanIdent | ta.Sequence[CanIdent]
22
+ ##
23
+
24
+
25
+ def _coerce_name_parts(o: ta.Iterable[Ident]) -> ta.Sequence[Ident]:
26
+ check.not_isinstance(o, str)
27
+ return check.not_empty(tuple(check.isinstance(e, Ident) for e in o))
28
+
29
+
30
+ class Name(Node, NameLike, lang.Final):
31
+ ps: ta.Sequence[Ident] = dc.xfield(coerce=_coerce_name_parts)
32
+
33
+
34
+ ##
35
+
36
+
37
+ @functools.singledispatch
38
+ def as_name(o: ta.Any) -> Name:
39
+ if isinstance(o, ta.Sequence):
40
+ return Name([as_ident(p) for p in o])
41
+ else:
42
+ raise TypeError(o)
43
+
44
+
45
+ @as_name.register
46
+ def _(n: Name) -> Name:
47
+ return n
48
+
49
+
50
+ @as_name.register
51
+ def _(o: Ident | str) -> Name:
52
+ return Name([as_ident(o)])
53
+
54
+
55
+ ##
56
+
57
+
58
+ CanName: ta.TypeAlias = ta.Union[
59
+ NameLike,
60
+ Name,
61
+ CanIdent,
62
+ ta.Sequence[CanIdent],
63
+ 'NameAccessor',
64
+ ]
65
+
66
+
67
+ class NameAccessor(NameLike, lang.Final):
68
+ def __init__(self, ps: tuple[str, ...] = ()) -> None:
69
+ super().__init__()
70
+ self.__query_name_parts__ = ps
71
+
72
+ def __repr__(self) -> str:
73
+ return f'{self.__class__.__name__}({self.__query_name_parts__!r})'
74
+
75
+ def __getattr__(self, s: str) -> 'NameAccessor':
76
+ return NameAccessor((*self.__query_name_parts__, s))
77
+
78
+ def __call__(self, o: CanName) -> Name:
79
+ n = as_name(o)
80
+ if (ps := self.__query_name_parts__):
81
+ n = Name(tuple(*tuple(Ident(p) for p in ps), *n.ps)) # type: ignore[arg-type]
82
+ return n
83
+
84
+
85
+ @as_name.register
86
+ def _(a: NameAccessor) -> Name:
87
+ return Name(tuple(Ident(p) for p in a.__query_name_parts__))
88
+
89
+
90
+ ##
19
91
 
20
92
 
21
93
  class NameBuilder(IdentBuilder):
94
+ @ta.final
22
95
  def name(self, o: CanName) -> Name:
23
- if isinstance(o, Name):
24
- return o
25
- elif isinstance(o, (Ident, str)):
26
- return Name([self.ident(o)])
27
- elif isinstance(o, ta.Sequence):
28
- return Name([self.ident(p) for p in o])
29
- else:
30
- raise TypeError(o)
96
+ return as_name(o)
31
97
 
32
98
  @ta.final
33
- def n(self, o: CanName) -> Name:
34
- return self.name(o)
99
+ @property
100
+ def n(self) -> NameAccessor:
101
+ return NameAccessor()