omlish 0.0.0.dev132__py3-none-any.whl → 0.0.0.dev177__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +7 -5
  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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +13 -11
  188. {omlish-0.0.0.dev132.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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {omlish-0.0.0.dev132.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()