omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (226) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/__about__.py +15 -15
  3. omlish/argparse/all.py +17 -9
  4. omlish/argparse/cli.py +16 -3
  5. omlish/argparse/utils.py +21 -0
  6. omlish/asyncs/asyncio/rlock.py +110 -0
  7. omlish/asyncs/asyncio/sync.py +43 -0
  8. omlish/asyncs/asyncio/utils.py +2 -0
  9. omlish/asyncs/sync.py +25 -0
  10. omlish/bootstrap/_marshal.py +1 -1
  11. omlish/bootstrap/diag.py +12 -21
  12. omlish/bootstrap/main.py +2 -5
  13. omlish/bootstrap/sys.py +27 -28
  14. omlish/cexts/__init__.py +0 -0
  15. omlish/cexts/include/omlish/omlish.hh +1 -0
  16. omlish/collections/__init__.py +13 -1
  17. omlish/collections/attrregistry.py +210 -0
  18. omlish/collections/cache/impl.py +1 -0
  19. omlish/collections/identity.py +1 -0
  20. omlish/collections/mappings.py +28 -0
  21. omlish/collections/trie.py +5 -1
  22. omlish/collections/utils.py +77 -0
  23. omlish/concurrent/all.py +2 -1
  24. omlish/concurrent/futures.py +25 -0
  25. omlish/concurrent/threadlets.py +1 -1
  26. omlish/daemons/reparent.py +2 -3
  27. omlish/daemons/spawning.py +2 -3
  28. omlish/dataclasses/__init__.py +2 -0
  29. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  30. omlish/dataclasses/impl/api/classes/make.py +3 -0
  31. omlish/dataclasses/impl/concerns/repr.py +15 -2
  32. omlish/dataclasses/impl/configs.py +97 -37
  33. omlish/dataclasses/impl/generation/compilation.py +21 -19
  34. omlish/dataclasses/impl/generation/globals.py +1 -0
  35. omlish/dataclasses/impl/generation/ops.py +1 -0
  36. omlish/dataclasses/impl/generation/processor.py +105 -24
  37. omlish/dataclasses/impl/processing/base.py +8 -0
  38. omlish/dataclasses/impl/processing/driving.py +8 -8
  39. omlish/dataclasses/specs.py +1 -0
  40. omlish/dataclasses/tools/modifiers.py +5 -0
  41. omlish/diag/cmds/__init__.py +0 -0
  42. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  43. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  44. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  45. omlish/diag/pycharm.py +16 -2
  46. omlish/diag/pydevd.py +58 -40
  47. omlish/diag/replserver/console.py +1 -1
  48. omlish/dispatch/__init__.py +18 -12
  49. omlish/dispatch/methods.py +50 -140
  50. omlish/dom/rendering.py +1 -1
  51. omlish/formats/dotenv.py +1 -1
  52. omlish/formats/json/stream/__init__.py +13 -0
  53. omlish/funcs/guard.py +225 -0
  54. omlish/graphs/dot/rendering.py +1 -1
  55. omlish/http/all.py +44 -4
  56. omlish/http/clients/asyncs.py +33 -27
  57. omlish/http/clients/base.py +17 -1
  58. omlish/http/clients/coro/__init__.py +0 -0
  59. omlish/http/clients/coro/sync.py +171 -0
  60. omlish/http/clients/default.py +208 -29
  61. omlish/http/clients/executor.py +56 -0
  62. omlish/http/clients/httpx.py +72 -11
  63. omlish/http/clients/middleware.py +181 -0
  64. omlish/http/clients/sync.py +33 -27
  65. omlish/http/clients/syncasync.py +49 -0
  66. omlish/http/clients/urllib.py +6 -3
  67. omlish/http/coro/client/connection.py +15 -6
  68. omlish/http/coro/io.py +2 -0
  69. omlish/http/flasky/__init__.py +40 -0
  70. omlish/http/flasky/_compat.py +2 -0
  71. omlish/http/flasky/api.py +82 -0
  72. omlish/http/flasky/app.py +203 -0
  73. omlish/http/flasky/cvs.py +59 -0
  74. omlish/http/flasky/requests.py +20 -0
  75. omlish/http/flasky/responses.py +23 -0
  76. omlish/http/flasky/routes.py +23 -0
  77. omlish/http/flasky/types.py +15 -0
  78. omlish/http/urls.py +67 -0
  79. omlish/inject/__init__.py +38 -18
  80. omlish/inject/_dataclasses.py +4986 -0
  81. omlish/inject/binder.py +4 -48
  82. omlish/inject/elements.py +27 -0
  83. omlish/inject/helpers/__init__.py +0 -0
  84. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  85. omlish/inject/{tags.py → helpers/id.py} +2 -2
  86. omlish/inject/helpers/multis.py +143 -0
  87. omlish/inject/helpers/wrappers.py +54 -0
  88. omlish/inject/impl/elements.py +47 -17
  89. omlish/inject/impl/injector.py +20 -19
  90. omlish/inject/impl/inspect.py +10 -1
  91. omlish/inject/impl/maysync.py +3 -4
  92. omlish/inject/impl/multis.py +3 -0
  93. omlish/inject/impl/sync.py +3 -4
  94. omlish/inject/injector.py +31 -2
  95. omlish/inject/inspect.py +35 -0
  96. omlish/inject/maysync.py +2 -4
  97. omlish/inject/multis.py +8 -0
  98. omlish/inject/overrides.py +3 -3
  99. omlish/inject/privates.py +6 -0
  100. omlish/inject/providers.py +3 -2
  101. omlish/inject/sync.py +5 -4
  102. omlish/io/buffers.py +118 -0
  103. omlish/io/readers.py +29 -0
  104. omlish/iterators/transforms.py +2 -2
  105. omlish/lang/__init__.py +178 -97
  106. omlish/lang/_asyncs.cc +186 -0
  107. omlish/lang/asyncs.py +17 -0
  108. omlish/lang/casing.py +11 -0
  109. omlish/lang/contextmanagers.py +28 -4
  110. omlish/lang/functions.py +31 -22
  111. omlish/lang/imports/_capture.cc +11 -9
  112. omlish/lang/imports/capture.py +363 -170
  113. omlish/lang/imports/proxy.py +455 -152
  114. omlish/lang/lazyglobals.py +22 -9
  115. omlish/lang/params.py +17 -0
  116. omlish/lang/recursion.py +0 -1
  117. omlish/lang/sequences.py +124 -0
  118. omlish/lite/abstract.py +54 -24
  119. omlish/lite/asyncs.py +2 -2
  120. omlish/lite/attrops.py +2 -0
  121. omlish/lite/contextmanagers.py +4 -4
  122. omlish/lite/dataclasses.py +44 -0
  123. omlish/lite/maybes.py +8 -0
  124. omlish/lite/maysync.py +1 -5
  125. omlish/lite/pycharm.py +1 -1
  126. omlish/lite/typing.py +6 -0
  127. omlish/logs/all.py +1 -1
  128. omlish/logs/utils.py +1 -1
  129. omlish/manifests/loading.py +2 -1
  130. omlish/marshal/__init__.py +33 -13
  131. omlish/marshal/_dataclasses.py +2774 -0
  132. omlish/marshal/base/configs.py +12 -0
  133. omlish/marshal/base/contexts.py +36 -21
  134. omlish/marshal/base/funcs.py +8 -11
  135. omlish/marshal/base/options.py +8 -0
  136. omlish/marshal/base/registries.py +146 -44
  137. omlish/marshal/base/types.py +40 -16
  138. omlish/marshal/composite/iterables.py +33 -20
  139. omlish/marshal/composite/literals.py +20 -18
  140. omlish/marshal/composite/mappings.py +36 -23
  141. omlish/marshal/composite/maybes.py +29 -19
  142. omlish/marshal/composite/newtypes.py +16 -16
  143. omlish/marshal/composite/optionals.py +14 -14
  144. omlish/marshal/composite/special.py +15 -15
  145. omlish/marshal/composite/unions/__init__.py +0 -0
  146. omlish/marshal/composite/unions/literals.py +93 -0
  147. omlish/marshal/composite/unions/primitives.py +103 -0
  148. omlish/marshal/factories/invalidate.py +18 -68
  149. omlish/marshal/factories/method.py +26 -0
  150. omlish/marshal/factories/moduleimport/factories.py +22 -65
  151. omlish/marshal/factories/multi.py +13 -25
  152. omlish/marshal/factories/recursive.py +42 -56
  153. omlish/marshal/factories/typecache.py +29 -74
  154. omlish/marshal/factories/typemap.py +42 -43
  155. omlish/marshal/objects/dataclasses.py +129 -106
  156. omlish/marshal/objects/marshal.py +18 -14
  157. omlish/marshal/objects/namedtuples.py +48 -42
  158. omlish/marshal/objects/unmarshal.py +19 -15
  159. omlish/marshal/polymorphism/marshal.py +9 -11
  160. omlish/marshal/polymorphism/metadata.py +16 -5
  161. omlish/marshal/polymorphism/standard.py +13 -1
  162. omlish/marshal/polymorphism/unions.py +15 -105
  163. omlish/marshal/polymorphism/unmarshal.py +9 -10
  164. omlish/marshal/singular/enums.py +14 -18
  165. omlish/marshal/standard.py +10 -6
  166. omlish/marshal/trivial/any.py +1 -1
  167. omlish/marshal/trivial/forbidden.py +21 -26
  168. omlish/metadata.py +23 -1
  169. omlish/os/forkhooks.py +4 -0
  170. omlish/os/pidfiles/pinning.py +2 -2
  171. omlish/reflect/types.py +1 -0
  172. omlish/secrets/marshal.py +1 -1
  173. omlish/specs/jmespath/__init__.py +12 -3
  174. omlish/specs/jmespath/_dataclasses.py +2893 -0
  175. omlish/specs/jmespath/ast.py +1 -1
  176. omlish/specs/jsonrpc/__init__.py +13 -0
  177. omlish/specs/jsonrpc/_marshal.py +32 -23
  178. omlish/specs/jsonrpc/conns.py +10 -7
  179. omlish/specs/jsonschema/_marshal.py +1 -1
  180. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  181. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  182. omlish/specs/openapi/_marshal.py +31 -22
  183. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  184. omlish/sql/queries/_marshal.py +2 -2
  185. omlish/sql/queries/rendering.py +1 -1
  186. omlish/sql/tabledefs/_marshal.py +1 -1
  187. omlish/subprocesses/base.py +4 -0
  188. omlish/subprocesses/editor.py +1 -1
  189. omlish/sync.py +155 -21
  190. omlish/term/alt.py +60 -0
  191. omlish/term/confirm.py +8 -8
  192. omlish/term/pager.py +235 -0
  193. omlish/term/terminfo.py +935 -0
  194. omlish/term/termstate.py +67 -0
  195. omlish/term/vt100/terminal.py +0 -3
  196. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  197. omlish/testing/pytest/plugins/skips.py +2 -1
  198. omlish/testing/unittest/main.py +3 -3
  199. omlish/text/docwrap/__init__.py +3 -0
  200. omlish/text/docwrap/__main__.py +11 -0
  201. omlish/text/docwrap/api.py +83 -0
  202. omlish/text/docwrap/cli.py +86 -0
  203. omlish/text/docwrap/groups.py +84 -0
  204. omlish/text/docwrap/lists.py +167 -0
  205. omlish/text/docwrap/parts.py +146 -0
  206. omlish/text/docwrap/reflowing.py +106 -0
  207. omlish/text/docwrap/rendering.py +151 -0
  208. omlish/text/docwrap/utils.py +11 -0
  209. omlish/text/docwrap/wrapping.py +59 -0
  210. omlish/text/filecache.py +2 -2
  211. omlish/text/lorem.py +6 -0
  212. omlish/text/parts.py +2 -2
  213. omlish/text/textwrap.py +51 -0
  214. omlish/typedvalues/marshal.py +85 -59
  215. omlish/typedvalues/values.py +2 -1
  216. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
  217. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
  218. omlish/dataclasses/impl/generation/mangling.py +0 -18
  219. omlish/funcs/match.py +0 -227
  220. omlish/marshal/factories/match.py +0 -34
  221. omlish/marshal/factories/simple.py +0 -28
  222. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  223. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
  224. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
  225. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
  226. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,14 @@
1
+ """
2
+ TODO:
3
+ - better support anonymous / weakref'd / unlaoded modules / etc.
4
+ - anonymous=False on class spec?
5
+ """
1
6
  import dataclasses as dc
7
+ import threading
2
8
  import typing as ta
3
9
 
10
+ from ... import check
11
+
4
12
 
5
13
  ##
6
14
 
@@ -19,52 +27,104 @@ DEFAULT_PACKAGE_CONFIG = PackageConfig()
19
27
  ##
20
28
 
21
29
 
22
- def init_package(
23
- init_globals: ta.MutableMapping[str, ta.Any],
24
- *,
25
- codegen: bool = False,
26
- ) -> None:
27
- if init_globals['__name__'] != init_globals['__package__']:
28
- raise NameError('Must call dataclasses.init_package from __init__')
30
+ @dc.dataclass(frozen=True)
31
+ class NamedPackageConfig:
32
+ pkg: str | None
33
+ cfg: PackageConfig
34
+
35
+
36
+ DEFAULT_NAMED_PACKAGE_CONFIG = NamedPackageConfig(None, DEFAULT_PACKAGE_CONFIG)
29
37
 
30
38
 
31
39
  ##
32
40
 
33
41
 
42
+ @ta.final
34
43
  class PackageConfigCache:
35
44
  def __init__(self) -> None:
36
45
  super().__init__()
37
46
 
38
- self._dct: dict[str, PackageConfig | None] = {}
39
-
40
- @ta.overload
41
- def get(self, pkg: str, default: PackageConfig) -> PackageConfig:
42
- ...
43
-
44
- @ta.overload
45
- def get(self, pkg: str, default: PackageConfig | None = None) -> PackageConfig | None:
46
- ...
47
-
48
- def get(self, pkg, default=None):
49
- # try:
50
- # return self._dct[pkg]
51
- # except KeyError:
52
- # pass
53
- #
54
- # try:
55
- # s = importlib.resources.read_text(pkg, PACKAGE_CONFIG_FILE_NAME)
56
- # except FileNotFoundError:
57
- # self._dct[pkg] = None
58
- # else:
59
- # c = PackageConfig.loads(s)
60
- # self._dct[pkg] = c
61
- # return c
62
- #
63
- # if '.' not in pkg:
64
- # return default
65
- #
66
- # return self.get(pkg.rpartition('.')[0], default)
67
- return None
47
+ self._lock = threading.Lock()
48
+ self._root = PackageConfigCache._Node('', None)
49
+ self._nodes: dict[str, PackageConfigCache._Node] = {}
50
+
51
+ @ta.final
52
+ class _Node:
53
+ def __init__(self, pkg: str, n_cfg: NamedPackageConfig | None) -> None:
54
+ self.pkg = pkg
55
+ self.n_cfg = n_cfg
56
+
57
+ self.children: dict[str, PackageConfigCache._Node] = {}
58
+
59
+ def __repr__(self) -> str:
60
+ return f'{self.__class__.__name__}(pkg={self.pkg!r}, n_cfg={self.n_cfg!r})'
61
+
62
+ def _navigate(self, *parts: str) -> _Node:
63
+ node = self._root
64
+ for i, p in enumerate(parts):
65
+ if (child := node.children.get(p)) is None:
66
+ child_pkg = '.'.join(parts[:i + 1])
67
+ check.not_in(child_pkg, self._nodes)
68
+ child = node.children[p] = self._nodes[child_pkg] = PackageConfigCache._Node(child_pkg, node.n_cfg)
69
+ node = child
70
+ return node
71
+
72
+ def put(self, pkg: str, cfg: PackageConfig) -> None:
73
+ n_cfg = NamedPackageConfig(pkg, cfg)
74
+ check.non_empty_str(pkg)
75
+ with self._lock:
76
+ check.not_in(pkg, self._nodes)
77
+ parts = pkg.split('.')
78
+ parent = self._navigate(*parts[:-1])
79
+ check.not_in(parts[-1], parent.children)
80
+ parent.children[parts[-1]] = self._nodes[pkg] = PackageConfigCache._Node(pkg, n_cfg)
81
+
82
+ def get(self, pkg: str) -> NamedPackageConfig | None:
83
+ if not pkg:
84
+ return None
85
+
86
+ try:
87
+ node = self._nodes[pkg]
88
+ except KeyError:
89
+ pass
90
+ else:
91
+ return node.n_cfg
92
+
93
+ # Flimsy - if no config anywhere for root package then don't cache. Want to support unlimited anonymous modules
94
+ # which may be unloaded without polluting cache forever.
95
+ parts = pkg.split('.')
96
+ if parts[0] not in self._root.children:
97
+ return None
98
+
99
+ with self._lock:
100
+ try:
101
+ node = self._nodes[pkg]
102
+ except KeyError:
103
+ pass
104
+ else:
105
+ return node.n_cfg
106
+
107
+ node = self._navigate(*pkg.split('.'))
108
+ return node.n_cfg
68
109
 
69
110
 
70
111
  PACKAGE_CONFIG_CACHE = PackageConfigCache()
112
+
113
+
114
+ ##
115
+
116
+
117
+ def init_package(
118
+ init_globals: ta.MutableMapping[str, ta.Any],
119
+ *,
120
+ codegen: bool = False,
121
+ ) -> None:
122
+ pkg = check.non_empty_str(init_globals['__package__'])
123
+ if init_globals['__name__'] not in (pkg, '__main__'):
124
+ raise NameError('Must call dataclasses.init_package from __init__')
125
+
126
+ pkg_cfg = PackageConfig(
127
+ codegen=codegen,
128
+ )
129
+
130
+ PACKAGE_CONFIG_CACHE.put(pkg, pkg_cfg)
@@ -64,7 +64,6 @@ class OpCompiler:
64
64
  f'import {i}'
65
65
  for i in FN_GLOBAL_IMPORTS
66
66
  ],
67
- '\n',
68
67
  ]
69
68
 
70
69
  def globals_ns(self) -> ta.Mapping[str, ta.Any]:
@@ -84,11 +83,14 @@ class OpCompiler:
84
83
  def style(self) -> Style:
85
84
  return self._style
86
85
 
87
- @dc.dataclass(frozen=True)
86
+ @dc.dataclass(frozen=True, kw_only=True)
88
87
  class CompileResult:
89
88
  fn_name: str
90
- params: ta.Sequence[str]
91
- src: str
89
+ fn_params: ta.Sequence[str]
90
+
91
+ hdr_lines: ta.Sequence[str]
92
+ fn_lines: ta.Sequence[str]
93
+
92
94
  refs: frozenset[Ref]
93
95
 
94
96
  @dc.dataclass(frozen=True)
@@ -137,7 +139,10 @@ class OpCompiler:
137
139
  ) -> CompileResult:
138
140
  body_lines: list[str] = []
139
141
 
140
- for op in ops:
142
+ for i, op in enumerate(ops):
143
+ if i:
144
+ body_lines.append('')
145
+
141
146
  if isinstance(op, SetAttrOp):
142
147
  if isinstance(v := op.value, OpRef):
143
148
  vs = v.ident()
@@ -202,8 +207,6 @@ class OpCompiler:
202
207
  else:
203
208
  raise TypeError(op)
204
209
 
205
- body_lines.append('')
206
-
207
210
  #
208
211
 
209
212
  refs = frozenset.union(*[get_op_refs(o) for o in ops])
@@ -226,14 +229,12 @@ class OpCompiler:
226
229
  src=f'{k.ident}={v.src}' if not v.src.startswith('.') else k.ident,
227
230
  noqa=k.ident != k.ident.lower() or not v.src.startswith('.'),
228
231
  )
229
- for k, v in FN_GLOBALS.items()
232
+ for k, v in sorted(FN_GLOBALS.items(), key=lambda t: t[0])
230
233
  ])
231
234
 
232
- lines: list[str] = []
233
-
234
- lines.extend(self._style.header_lines())
235
+ #
235
236
 
236
- lines.extend([
237
+ fn_lines = [
237
238
  f'def {fn_name}(',
238
239
  f' *,',
239
240
  *[
@@ -245,15 +246,16 @@ class OpCompiler:
245
246
  f' {l}'
246
247
  for l in body_lines
247
248
  ],
248
- ])
249
+ ]
249
250
 
250
251
  #
251
252
 
252
- src = '\n'.join(lines)
253
-
254
253
  return self.CompileResult(
255
- fn_name,
256
- [p.name for p in params],
257
- src,
258
- refs,
254
+ fn_name=fn_name,
255
+ fn_params=[p.name for p in params],
256
+
257
+ hdr_lines=self._style.header_lines(),
258
+ fn_lines=fn_lines,
259
+
260
+ refs=refs,
259
261
  )
@@ -12,6 +12,7 @@ from .idents import IDENT_PREFIX
12
12
  ##
13
13
 
14
14
 
15
+ @ta.final
15
16
  class FnGlobal(ta.NamedTuple):
16
17
  ident: str
17
18
 
@@ -16,6 +16,7 @@ class _OpRef(ta.NamedTuple, ta.Generic[T]):
16
16
  name: str
17
17
 
18
18
 
19
+ @ta.final
19
20
  class OpRef(_OpRef[T]):
20
21
  def __repr__(self) -> str:
21
22
  return f'OpRef(name={self.name!r})'
@@ -10,6 +10,7 @@ import typing as ta
10
10
 
11
11
  from .... import check
12
12
  from .... import lang
13
+ from ....logs import all as logs
13
14
  from ..processing.base import ProcessingContext
14
15
  from ..processing.base import ProcessingOption
15
16
  from ..processing.base import Processor
@@ -21,7 +22,6 @@ from .execution import OpExecutor
21
22
  from .globals import FN_GLOBALS
22
23
  from .globals import FnGlobal
23
24
  from .idents import CLS_IDENT
24
- from .mangling import IDENT_MANGLER
25
25
  from .ops import Op
26
26
  from .ops import OpRef
27
27
  from .ops import OpRefMap
@@ -30,6 +30,9 @@ from .registry import all_generator_types
30
30
  from .registry import generator_type_for_plan_type
31
31
 
32
32
 
33
+ log = logs.get_module_logger(globals())
34
+
35
+
33
36
  ##
34
37
 
35
38
 
@@ -38,9 +41,10 @@ class PlanOnly(ProcessingOption):
38
41
  b: bool
39
42
 
40
43
 
41
- @dc.dataclass(frozen=True)
42
- class Verbose(ProcessingOption):
43
- b: bool
44
+ @dc.dataclass(frozen=True, kw_only=True)
45
+ class Verbosity(ProcessingOption):
46
+ warn: bool = False
47
+ debug: bool = False
44
48
 
45
49
 
46
50
  class CompileCallback(ta.Protocol):
@@ -53,13 +57,21 @@ class CompileCallback(ta.Protocol):
53
57
  ...
54
58
 
55
59
 
56
- @dc.dataclass(frozen=True)
60
+ @dc.dataclass(frozen=True, kw_only=True)
57
61
  class Codegen(ProcessingOption):
58
- callback: CompileCallback
62
+ style: ta.Literal['jit', 'aot'] = 'jit'
63
+
64
+ force: bool = False
65
+ callback: CompileCallback | None = None
66
+
67
+
68
+ ##
59
69
 
60
70
 
61
71
  @register_processor_type(priority=ProcessorPriority.GENERATION)
62
72
  class GeneratorProcessor(Processor):
73
+ PROCESS_FN_NAME: ta.ClassVar[str] = '_process_dataclass'
74
+
63
75
  class Mode(lang.Abstract):
64
76
  @abc.abstractmethod
65
77
  def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
@@ -85,36 +97,52 @@ class GeneratorProcessor(Processor):
85
97
 
86
98
  self._codegen = codegen
87
99
 
100
+ @classmethod
101
+ def build_standard_kwargs(cls, dc_cls: type) -> dict[str, ta.Any]:
102
+ kw: dict = {CLS_IDENT: dc_cls}
103
+ kw.update({
104
+ k.ident: v.value
105
+ for k, v in FN_GLOBALS.items()
106
+ })
107
+ return kw
108
+
88
109
  def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
89
- compiler = OpCompiler(
90
- OpCompiler.AotStyle(),
91
- # OpCompiler.JitStyle(),
92
- )
110
+ style: OpCompiler.Style = {
111
+ 'jit': OpCompiler.JitStyle,
112
+ 'aot': OpCompiler.AotStyle,
113
+ }[self._codegen.style]()
93
114
 
94
- fn_name = '_process_dataclass__' + IDENT_MANGLER.mangle(cls.__qualname__)
115
+ compiler = OpCompiler(style)
95
116
 
96
117
  comp = compiler.compile(
97
- fn_name,
118
+ GeneratorProcessor.PROCESS_FN_NAME,
98
119
  gp.ops(),
99
120
  )
100
121
 
101
- if (vo := gp._ctx.option(Verbose)) is not None and vo.b: # noqa
122
+ comp_src = '\n'.join([
123
+ *comp.hdr_lines,
124
+ *(['', ''] if comp.hdr_lines else []),
125
+ *comp.fn_lines,
126
+ ])
127
+
128
+ if (vo := gp._ctx.option(Verbosity)) is not None and vo.debug: # noqa
102
129
  print(gp.prepare().plans.render(), file=sys.stderr)
103
130
  print(file=sys.stderr)
104
- print(comp.src, file=sys.stderr)
131
+ print(comp_src, file=sys.stderr)
105
132
  print(file=sys.stderr)
106
133
 
107
134
  ns: dict = {}
108
135
  ns.update(compiler.style.globals_ns()) # noqa
109
136
 
110
- exec(comp.src, ns)
111
- o_fn = ns[comp.fn_name]
137
+ exec(comp_src, ns)
138
+ o_fn = ns[GeneratorProcessor.PROCESS_FN_NAME]
112
139
 
113
140
  if cls.__module__ in sys.modules:
114
141
  gl = sys.modules[cls.__module__].__dict__
115
142
  else:
116
143
  gl = {}
117
144
 
145
+ # TODO: comment why lol
118
146
  fn = lang.new_function(**{
119
147
  **lang.new_function_kwargs(o_fn),
120
148
  **dict(
@@ -122,12 +150,8 @@ class GeneratorProcessor(Processor):
122
150
  ),
123
151
  })
124
152
 
125
- kw: dict = {CLS_IDENT: cls}
126
- kw.update({
127
- k.ident: v.value
128
- for k, v in FN_GLOBALS.items()
129
- # if v.src.startswith('.')
130
- })
153
+ kw = self.build_standard_kwargs(cls)
154
+
131
155
  orm = gp.prepare().ref_map
132
156
  for r in comp.refs:
133
157
  if isinstance(r, OpRef):
@@ -140,7 +164,7 @@ class GeneratorProcessor(Processor):
140
164
  fn(**kw)
141
165
 
142
166
  if (cg := self._codegen) is not None and (cb := cg.callback) is not None:
143
- cb(
167
+ cb( # noqa
144
168
  gp._ctx, # noqa
145
169
  gp.prepare(),
146
170
  comp,
@@ -194,13 +218,70 @@ class GeneratorProcessor(Processor):
194
218
 
195
219
  #
196
220
 
221
+ def _process_from_codegen(self, cls: type) -> bool:
222
+ cg_pkg = check.not_none(self._ctx.pkg_cfg.pkg)
223
+ cg_mod_spec = f'{cg_pkg}._dataclasses'
224
+
225
+ try:
226
+ __import__(cg_mod_spec)
227
+ except ImportError:
228
+ if (vo := self._ctx.option(Verbosity)) is not None and vo.warn: # noqa
229
+ log.warning(lambda: f'Codegen module missing for {cls.__module__}.{cls.__qualname__} at {cg_mod_spec}')
230
+ return False
231
+
232
+ cg_mod = sys.modules[cg_mod_spec]
233
+ cg_fn_reg = cg_mod.REGISTRY
234
+
235
+ #
236
+
237
+ prep = self.prepare()
238
+ prep_plan_repr = repr(prep.plans)
239
+
240
+ #
241
+
242
+ try:
243
+ cg_reg_item = cg_fn_reg[prep_plan_repr]
244
+ except KeyError:
245
+ if (vo := self._ctx.option(Verbosity)) is not None and vo.warn: # noqa
246
+ log.warning(lambda: f'Codegen missing for {cls.__module__}.{cls.__qualname__} in {cg_mod_spec}')
247
+ return False
248
+
249
+ cg_kw, cg_fn = cg_reg_item
250
+
251
+ #
252
+
253
+ ref_map = {
254
+ ref.ident(): v
255
+ for ref, v in prep.ref_map.items()
256
+ }
257
+
258
+ #
259
+
260
+ fn_kw = {
261
+ **GeneratorProcessor.CompilerMode.build_standard_kwargs(cls),
262
+ **{k: ref_map[k] for k in cg_kw['op_ref_idents']},
263
+ }
264
+
265
+ fn = cg_fn()
266
+ fn(**fn_kw)
267
+
268
+ return True
269
+
270
+ #
271
+
197
272
  def process(self, cls: type) -> type:
198
273
  if (po := self._ctx.option(PlanOnly)) is not None and po.b:
199
274
  self.prepare()
200
275
  return cls
201
276
 
277
+ cg = self._ctx.option(Codegen)
278
+
279
+ if not (cg is not None and cg.force) and self._ctx.pkg_cfg.cfg.codegen:
280
+ if self._process_from_codegen(cls):
281
+ return cls
282
+
202
283
  mode: GeneratorProcessor.Mode
203
- if (cg := self._ctx.option(Codegen)) is not None: # noqa
284
+ if cg is not None: # noqa
204
285
  mode = GeneratorProcessor.CompilerMode(codegen=cg)
205
286
  else:
206
287
  mode = GeneratorProcessor.ExecutorMode()
@@ -10,6 +10,8 @@ import typing as ta
10
10
  from .... import check
11
11
  from .... import lang
12
12
  from ...specs import ClassSpec
13
+ from ..configs import DEFAULT_NAMED_PACKAGE_CONFIG
14
+ from ..configs import NamedPackageConfig
13
15
 
14
16
 
15
17
  T = ta.TypeVar('T')
@@ -33,6 +35,7 @@ class ProcessingContext:
33
35
  cs: ClassSpec,
34
36
  item_factories: ta.Mapping[type, ProcessingContextItemFactory],
35
37
  *,
38
+ pkg_cfg: NamedPackageConfig = DEFAULT_NAMED_PACKAGE_CONFIG,
36
39
  options: ta.Sequence[ProcessingOption] | None = None,
37
40
  ) -> None:
38
41
  super().__init__()
@@ -40,6 +43,7 @@ class ProcessingContext:
40
43
  self._cls = cls
41
44
  self._cs = cs
42
45
  self._item_factories = item_factories
46
+ self._pkg_cfg = pkg_cfg
43
47
 
44
48
  options_dct: dict = {}
45
49
  for o in options or ():
@@ -57,6 +61,10 @@ class ProcessingContext:
57
61
  def cs(self) -> ClassSpec:
58
62
  return self._cs
59
63
 
64
+ @property
65
+ def pkg_cfg(self) -> NamedPackageConfig:
66
+ return self._pkg_cfg
67
+
60
68
  def __getitem__(self, ty: type[T]) -> T:
61
69
  try:
62
70
  return self._items[ty]
@@ -5,7 +5,7 @@ import typing as ta
5
5
  from .... import lang
6
6
  from ...specs import ClassSpec
7
7
  from .. import concerns as _concerns # noqa # imported for registration
8
- from ..configs import DEFAULT_PACKAGE_CONFIG
8
+ from ..configs import DEFAULT_NAMED_PACKAGE_CONFIG
9
9
  from ..configs import PACKAGE_CONFIG_CACHE
10
10
  from ..generation import processor as gp
11
11
  from .base import ProcessingContext
@@ -41,21 +41,20 @@ def drive_cls_processing(
41
41
  cs: ClassSpec,
42
42
  *,
43
43
  plan_only: bool = False,
44
- verbose: bool = False,
44
+ warn: bool = False,
45
+ debug: bool = False,
45
46
  ) -> type:
46
47
  options: list[ProcessingOption] = list(_OPTIONS_CONTEXT_VAR.get())
47
48
  if plan_only:
48
49
  options.append(gp.PlanOnly(True))
49
- if verbose:
50
- options.append(gp.Verbose(True))
50
+ if warn or debug:
51
+ options.append(gp.Verbosity(warn=warn, debug=debug))
51
52
 
52
53
  #
53
54
 
54
- pkg_config = DEFAULT_PACKAGE_CONFIG
55
55
  cls_mod = cls.__module__
56
- if '.' in cls_mod:
57
- cls_pkg = cls_mod.rpartition('.')[0]
58
- pkg_cfg = PACKAGE_CONFIG_CACHE.get(cls_pkg, pkg_config) # noqa
56
+ cls_pkg = cls_mod.rpartition('.')[0]
57
+ pkg_cfg = lang.coalesce(PACKAGE_CONFIG_CACHE.get(cls_pkg), DEFAULT_NAMED_PACKAGE_CONFIG)
59
58
 
60
59
  #
61
60
 
@@ -63,6 +62,7 @@ def drive_cls_processing(
63
62
  cls,
64
63
  cs,
65
64
  all_processing_context_item_factories(),
65
+ pkg_cfg=pkg_cfg,
66
66
  options=options,
67
67
  )
68
68
 
@@ -216,6 +216,7 @@ class ClassSpec(_SpecBase, lang.Final):
216
216
 
217
217
  repr_id: bool = False
218
218
  terse_repr: bool = False
219
+ default_repr_fn: ReprFn | None = None
219
220
 
220
221
  allow_redundant_decorator: bool = False
221
222
 
@@ -3,6 +3,7 @@ import typing as ta
3
3
 
4
4
  from ... import check
5
5
  from ... import lang
6
+ from ...lite.dataclasses import is_immediate_dataclass
6
7
 
7
8
 
8
9
  if ta.TYPE_CHECKING:
@@ -40,6 +41,8 @@ def update_fields(
40
41
  def inner(cls):
41
42
  if issubclass(cls, meta.DataMeta):
42
43
  raise TypeError('update_fields() cannot be used on DataMeta subclasses')
44
+ if is_immediate_dataclass(cls):
45
+ raise TypeError('update_fields() cannot be used on already processed dataclasses')
43
46
 
44
47
  if fields is None:
45
48
  for a, v in list(cls.__dict__.items()):
@@ -51,6 +54,8 @@ def update_fields(
51
54
  try:
52
55
  v = cls.__dict__[a]
53
56
  except KeyError:
57
+ if hasattr(cls, a):
58
+ raise TypeError('update_fields() cannot be used on parent dataclass fields') from None
54
59
  v = dc.field()
55
60
  else:
56
61
  if not isinstance(v, dc.Field):
File without changes
@@ -7,12 +7,12 @@ import dataclasses as dc
7
7
  import json
8
8
  import typing as ta
9
9
 
10
- from ..lite.check import check
11
- from ..lite.marshal import OBJ_MARSHALER_FIELD_KEY
12
- from ..lite.marshal import unmarshal_obj
13
- from ..subprocesses.run import SubprocessRun
14
- from ..subprocesses.run import SubprocessRunnable
15
- from ..subprocesses.run import SubprocessRunOutput
10
+ from ...lite.check import check
11
+ from ...lite.marshal import OBJ_MARSHALER_FIELD_KEY
12
+ from ...lite.marshal import unmarshal_obj
13
+ from ...subprocesses.run import SubprocessRun
14
+ from ...subprocesses.run import SubprocessRunnable
15
+ from ...subprocesses.run import SubprocessRunOutput
16
16
 
17
17
 
18
18
  ##
@@ -7,12 +7,12 @@ import dataclasses as dc
7
7
  import enum
8
8
  import typing as ta
9
9
 
10
- from ..lite.check import check
11
- from ..lite.dataclasses import dataclass_repr_omit_falsey
12
- from ..lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
13
- from ..subprocesses.run import SubprocessRun
14
- from ..subprocesses.run import SubprocessRunnable
15
- from ..subprocesses.run import SubprocessRunOutput
10
+ from ...lite.check import check
11
+ from ...lite.dataclasses import dataclass_repr_omit_falsey
12
+ from ...lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
13
+ from ...subprocesses.run import SubprocessRun
14
+ from ...subprocesses.run import SubprocessRunnable
15
+ from ...subprocesses.run import SubprocessRunOutput
16
16
 
17
17
 
18
18
  ##
@@ -4,12 +4,12 @@ import dataclasses as dc
4
4
  import os
5
5
  import typing as ta
6
6
 
7
- from ..lite.check import check
8
- from ..lite.timeouts import Timeout
9
- from ..subprocesses.run import SubprocessRun
10
- from ..subprocesses.run import SubprocessRunnable
11
- from ..subprocesses.run import SubprocessRunOutput
12
- from ..subprocesses.sync import subprocesses
7
+ from ...lite.check import check
8
+ from ...lite.timeouts import Timeout
9
+ from ...subprocesses.run import SubprocessRun
10
+ from ...subprocesses.run import SubprocessRunnable
11
+ from ...subprocesses.run import SubprocessRunOutput
12
+ from ...subprocesses.sync import subprocesses
13
13
 
14
14
 
15
15
  ##
omlish/diag/pycharm.py CHANGED
@@ -191,9 +191,23 @@ def pycharm_remote_debugger_attach(prd: PycharmRemoteDebugger) -> None:
191
191
  else:
192
192
  pydevd_pycharm = _import_pydevd_pycharm(version=version)
193
193
 
194
+ import inspect
195
+ st_sig = inspect.signature(pydevd_pycharm.settrace)
196
+
197
+ kw: dict = {}
198
+ if 'stdoutToServer' in st_sig.parameters:
199
+ kw.update(
200
+ stdoutToServer=True,
201
+ stderrToServer=True,
202
+ )
203
+ else:
204
+ kw.update(
205
+ stdout_to_server=True,
206
+ stderr_to_server=True,
207
+ )
208
+
194
209
  pydevd_pycharm.settrace(
195
210
  host,
196
211
  port=prd.port,
197
- stdoutToServer=True,
198
- stderrToServer=True,
212
+ **kw,
199
213
  )