omlish 0.0.0.dev423__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 (464) hide show
  1. omlish/{.manifests.json → .omlish-manifests.json} +12 -0
  2. omlish/__about__.py +21 -18
  3. omlish/argparse/all.py +17 -9
  4. omlish/argparse/cli.py +16 -3
  5. omlish/argparse/utils.py +21 -0
  6. omlish/asyncs/all.py +0 -13
  7. omlish/asyncs/asyncio/rlock.py +110 -0
  8. omlish/asyncs/asyncio/subprocesses.py +2 -2
  9. omlish/asyncs/asyncio/sync.py +43 -0
  10. omlish/asyncs/asyncio/utils.py +2 -0
  11. omlish/asyncs/ioproxy/proxy.py +1 -1
  12. omlish/asyncs/sync.py +25 -0
  13. omlish/bootstrap/_marshal.py +1 -1
  14. omlish/bootstrap/diag.py +12 -21
  15. omlish/bootstrap/main.py +2 -5
  16. omlish/bootstrap/sys.py +27 -28
  17. omlish/c3.py +4 -1
  18. omlish/cexts/include/omlish/omlish.hh +1 -0
  19. omlish/collections/__init__.py +13 -1
  20. omlish/collections/attrregistry.py +210 -0
  21. omlish/collections/cache/impl.py +3 -2
  22. omlish/collections/identity.py +1 -0
  23. omlish/collections/mappings.py +28 -0
  24. omlish/collections/trie.py +5 -1
  25. omlish/collections/utils.py +77 -0
  26. omlish/concurrent/__init__.py +0 -11
  27. omlish/concurrent/all.py +18 -0
  28. omlish/concurrent/futures.py +25 -0
  29. omlish/concurrent/threadlets.py +1 -1
  30. omlish/configs/processing/flattening.py +1 -1
  31. omlish/configs/processing/merging.py +8 -6
  32. omlish/configs/types.py +1 -1
  33. omlish/daemons/__init__.py +70 -0
  34. omlish/daemons/daemon.py +2 -2
  35. omlish/daemons/launching.py +2 -2
  36. omlish/daemons/reparent.py +2 -3
  37. omlish/daemons/spawning.py +2 -3
  38. omlish/dataclasses/__init__.py +5 -5
  39. omlish/dataclasses/errors.py +1 -1
  40. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  41. omlish/dataclasses/impl/api/classes/make.py +4 -1
  42. omlish/dataclasses/impl/concerns/doc.py +1 -1
  43. omlish/dataclasses/impl/concerns/repr.py +15 -2
  44. omlish/dataclasses/impl/configs.py +97 -36
  45. omlish/dataclasses/impl/generation/compilation.py +21 -19
  46. omlish/dataclasses/impl/generation/globals.py +1 -0
  47. omlish/dataclasses/impl/generation/ops.py +1 -0
  48. omlish/dataclasses/impl/generation/processor.py +105 -24
  49. omlish/dataclasses/impl/processing/base.py +8 -0
  50. omlish/dataclasses/impl/processing/driving.py +8 -8
  51. omlish/dataclasses/specs.py +34 -2
  52. omlish/dataclasses/tools/as_.py +0 -12
  53. omlish/dataclasses/tools/modifiers.py +5 -0
  54. omlish/dataclasses/tools/static.py +1 -1
  55. omlish/diag/_pycharm/runhack.py +1 -1
  56. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  57. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  58. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  59. omlish/diag/procfs.py +4 -4
  60. omlish/diag/pycharm.py +16 -2
  61. omlish/diag/pydevd.py +58 -40
  62. omlish/diag/replserver/console.py +3 -3
  63. omlish/diag/replserver/server.py +2 -2
  64. omlish/dispatch/__init__.py +18 -12
  65. omlish/dispatch/methods.py +50 -140
  66. omlish/dom/rendering.py +1 -1
  67. omlish/formats/dotenv.py +8 -8
  68. omlish/formats/json/stream/__init__.py +18 -3
  69. omlish/formats/json/stream/building.py +2 -2
  70. omlish/formats/json/stream/lexing.py +401 -67
  71. omlish/formats/json/stream/parsing.py +32 -10
  72. omlish/formats/json/stream/rendering.py +6 -6
  73. omlish/formats/json/stream/utils.py +132 -30
  74. omlish/formats/json5/literals.py +7 -4
  75. omlish/formats/json5/parsing.py +33 -79
  76. omlish/formats/json5/stream.py +77 -0
  77. omlish/formats/logfmt.py +8 -2
  78. omlish/funcs/genmachine.py +2 -2
  79. omlish/funcs/guard.py +225 -0
  80. omlish/graphs/dot/rendering.py +1 -1
  81. omlish/http/all.py +122 -53
  82. omlish/http/asgi.py +2 -2
  83. omlish/http/clients/__init__.py +0 -34
  84. omlish/http/clients/asyncs.py +153 -0
  85. omlish/http/clients/base.py +63 -122
  86. omlish/http/clients/coro/sync.py +171 -0
  87. omlish/http/clients/default.py +209 -30
  88. omlish/http/clients/executor.py +56 -0
  89. omlish/http/clients/httpx.py +78 -13
  90. omlish/http/clients/middleware.py +181 -0
  91. omlish/http/clients/sync.py +151 -0
  92. omlish/http/clients/syncasync.py +49 -0
  93. omlish/http/clients/urllib.py +8 -5
  94. omlish/http/coro/client/{client.py → connection.py} +43 -37
  95. omlish/http/coro/client/headers.py +5 -5
  96. omlish/http/coro/client/response.py +37 -38
  97. omlish/http/coro/client/status.py +4 -4
  98. omlish/http/coro/{client/io.py → io.py} +19 -2
  99. omlish/http/coro/server/fdio.py +10 -9
  100. omlish/http/coro/server/server.py +14 -41
  101. omlish/http/coro/server/sockets.py +7 -6
  102. omlish/http/flasky/__init__.py +40 -0
  103. omlish/http/flasky/_compat.py +2 -0
  104. omlish/http/flasky/api.py +82 -0
  105. omlish/http/flasky/app.py +203 -0
  106. omlish/http/flasky/cvs.py +59 -0
  107. omlish/http/flasky/requests.py +20 -0
  108. omlish/http/flasky/responses.py +23 -0
  109. omlish/http/flasky/routes.py +23 -0
  110. omlish/http/flasky/types.py +15 -0
  111. omlish/http/handlers.py +3 -2
  112. omlish/http/headers.py +69 -35
  113. omlish/http/sse.py +1 -1
  114. omlish/http/urls.py +67 -0
  115. omlish/inject/__init__.py +173 -126
  116. omlish/inject/_dataclasses.py +4986 -0
  117. omlish/inject/binder.py +10 -49
  118. omlish/inject/elements.py +27 -0
  119. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  120. omlish/inject/{tags.py → helpers/id.py} +2 -2
  121. omlish/inject/helpers/multis.py +143 -0
  122. omlish/inject/helpers/wrappers.py +54 -0
  123. omlish/inject/impl/elements.py +52 -22
  124. omlish/inject/impl/injector.py +76 -49
  125. omlish/inject/impl/inspect.py +11 -1
  126. omlish/inject/impl/maysync.py +43 -0
  127. omlish/inject/impl/multis.py +10 -7
  128. omlish/inject/impl/privates.py +8 -8
  129. omlish/inject/impl/providers.py +23 -34
  130. omlish/inject/impl/providersmap.py +43 -0
  131. omlish/inject/impl/proxy.py +0 -2
  132. omlish/inject/impl/scopes.py +19 -23
  133. omlish/inject/impl/sync.py +41 -0
  134. omlish/inject/injector.py +37 -8
  135. omlish/inject/inspect.py +35 -0
  136. omlish/inject/listeners.py +4 -4
  137. omlish/inject/managed.py +54 -18
  138. omlish/inject/maysync.py +27 -0
  139. omlish/inject/multis.py +8 -0
  140. omlish/inject/overrides.py +3 -3
  141. omlish/inject/privates.py +6 -0
  142. omlish/inject/providers.py +8 -1
  143. omlish/inject/scopes.py +40 -12
  144. omlish/inject/sync.py +49 -0
  145. omlish/io/buffers.py +119 -1
  146. omlish/io/readers.py +29 -0
  147. omlish/iterators/__init__.py +28 -20
  148. omlish/iterators/transforms.py +204 -0
  149. omlish/lang/__init__.py +240 -129
  150. omlish/lang/_asyncs.cc +186 -0
  151. omlish/lang/asyncs.py +67 -43
  152. omlish/lang/{attrs.py → attrstorage.py} +15 -15
  153. omlish/lang/cached/property.py +2 -2
  154. omlish/lang/casing.py +11 -0
  155. omlish/lang/classes/bindable.py +2 -3
  156. omlish/lang/classes/restrict.py +8 -0
  157. omlish/lang/classes/simple.py +26 -4
  158. omlish/lang/collections.py +1 -1
  159. omlish/lang/contextmanagers.py +59 -9
  160. omlish/lang/functions.py +31 -33
  161. omlish/lang/imports/_capture.cc +103 -0
  162. omlish/lang/imports/capture.py +902 -0
  163. omlish/lang/imports/lazy.py +0 -25
  164. omlish/lang/imports/proxy.py +559 -0
  165. omlish/lang/iterables.py +2 -2
  166. omlish/lang/lazyglobals.py +49 -14
  167. omlish/lang/maybes.py +2 -1
  168. omlish/lang/maysync.py +2 -2
  169. omlish/lang/params.py +17 -0
  170. omlish/lang/recursion.py +0 -1
  171. omlish/lang/resources.py +1 -1
  172. omlish/lang/sequences.py +124 -0
  173. omlish/lifecycles/contextmanagers.py +1 -2
  174. omlish/lifecycles/controller.py +1 -2
  175. omlish/lite/abstract.py +54 -24
  176. omlish/lite/asyncs.py +146 -0
  177. omlish/lite/attrops.py +415 -0
  178. omlish/lite/cached.py +57 -1
  179. omlish/lite/contextmanagers.py +4 -4
  180. omlish/lite/dataclasses.py +55 -0
  181. omlish/lite/inject.py +5 -4
  182. omlish/lite/marshal.py +1 -0
  183. omlish/lite/maybes.py +10 -2
  184. omlish/lite/maysync.py +22 -5
  185. omlish/lite/pycharm.py +1 -1
  186. omlish/lite/strings.py +0 -7
  187. omlish/lite/timing.py +6 -3
  188. omlish/lite/typing.py +6 -0
  189. omlish/logs/_amalg.py +8 -0
  190. omlish/logs/all.py +59 -31
  191. omlish/logs/base.py +204 -0
  192. omlish/logs/contexts.py +171 -0
  193. omlish/logs/formatters.py +13 -0
  194. omlish/logs/infos.py +377 -0
  195. omlish/logs/levels.py +97 -0
  196. omlish/logs/modules.py +13 -0
  197. omlish/logs/protocols.py +32 -0
  198. omlish/logs/standard.py +20 -15
  199. omlish/logs/std/configs.py +29 -0
  200. omlish/logs/{filters.py → std/filters.py} +1 -1
  201. omlish/logs/std/formatters.py +25 -0
  202. omlish/logs/std/handlers.py +19 -0
  203. omlish/logs/{json.py → std/json.py} +2 -2
  204. omlish/logs/std/loggers.py +48 -0
  205. omlish/logs/{proxy.py → std/proxy.py} +3 -3
  206. omlish/logs/std/records.py +671 -0
  207. omlish/logs/typed/bindings.py +108 -37
  208. omlish/logs/typed/types.py +17 -1
  209. omlish/logs/typed/values.py +2 -2
  210. omlish/logs/utils.py +60 -4
  211. omlish/logs/warnings.py +8 -0
  212. omlish/manifests/loading.py +8 -1
  213. omlish/marshal/__init__.py +54 -22
  214. omlish/marshal/_dataclasses.py +2774 -0
  215. omlish/marshal/base/configs.py +12 -0
  216. omlish/marshal/base/contexts.py +36 -21
  217. omlish/marshal/base/funcs.py +8 -11
  218. omlish/marshal/base/options.py +8 -0
  219. omlish/marshal/base/registries.py +146 -44
  220. omlish/marshal/base/types.py +40 -16
  221. omlish/marshal/composite/iterables.py +33 -20
  222. omlish/marshal/composite/literals.py +20 -18
  223. omlish/marshal/composite/mappings.py +36 -23
  224. omlish/marshal/composite/maybes.py +29 -19
  225. omlish/marshal/composite/newtypes.py +16 -16
  226. omlish/marshal/composite/optionals.py +14 -14
  227. omlish/marshal/composite/special.py +15 -15
  228. omlish/marshal/composite/unions/literals.py +93 -0
  229. omlish/marshal/composite/unions/primitives.py +103 -0
  230. omlish/marshal/factories/invalidate.py +18 -68
  231. omlish/marshal/factories/method.py +26 -0
  232. omlish/marshal/factories/moduleimport/factories.py +22 -65
  233. omlish/marshal/factories/multi.py +13 -25
  234. omlish/marshal/factories/recursive.py +42 -56
  235. omlish/marshal/factories/typecache.py +29 -74
  236. omlish/marshal/factories/typemap.py +42 -43
  237. omlish/marshal/objects/dataclasses.py +129 -106
  238. omlish/marshal/objects/marshal.py +18 -14
  239. omlish/marshal/objects/namedtuples.py +48 -42
  240. omlish/marshal/objects/unmarshal.py +19 -15
  241. omlish/marshal/polymorphism/marshal.py +9 -11
  242. omlish/marshal/polymorphism/metadata.py +16 -5
  243. omlish/marshal/polymorphism/standard.py +13 -1
  244. omlish/marshal/polymorphism/unions.py +15 -105
  245. omlish/marshal/polymorphism/unmarshal.py +9 -10
  246. omlish/marshal/singular/enums.py +14 -18
  247. omlish/marshal/standard.py +10 -6
  248. omlish/marshal/trivial/any.py +1 -1
  249. omlish/marshal/trivial/forbidden.py +21 -26
  250. omlish/math/fixed.py +2 -2
  251. omlish/metadata.py +23 -1
  252. omlish/os/atomics.py +2 -2
  253. omlish/os/forkhooks.py +4 -0
  254. omlish/os/journald.py +3 -3
  255. omlish/os/pidfiles/pinning.py +2 -2
  256. omlish/reflect/ops.py +9 -0
  257. omlish/reflect/types.py +44 -8
  258. omlish/secrets/marshal.py +1 -1
  259. omlish/secrets/secrets.py +6 -3
  260. omlish/sockets/addresses.py +1 -1
  261. omlish/sockets/server/handlers.py +2 -2
  262. omlish/sockets/server/server.py +4 -3
  263. omlish/sockets/server/ssl.py +2 -2
  264. omlish/specs/jmespath/__init__.py +12 -3
  265. omlish/specs/jmespath/_dataclasses.py +2893 -0
  266. omlish/specs/jmespath/ast.py +1 -1
  267. omlish/specs/jsonrpc/__init__.py +13 -0
  268. omlish/specs/jsonrpc/_marshal.py +32 -23
  269. omlish/specs/jsonrpc/conns.py +10 -7
  270. omlish/specs/jsonschema/_marshal.py +1 -1
  271. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  272. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  273. omlish/specs/openapi/_marshal.py +31 -22
  274. omlish/sql/__init__.py +15 -20
  275. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  276. omlish/sql/queries/_marshal.py +3 -3
  277. omlish/sql/queries/params.py +1 -1
  278. omlish/sql/queries/rendering.py +1 -1
  279. omlish/sql/tabledefs/_marshal.py +1 -1
  280. omlish/subprocesses/all.py +135 -0
  281. omlish/subprocesses/base.py +8 -3
  282. omlish/subprocesses/editor.py +1 -1
  283. omlish/sync.py +181 -20
  284. omlish/term/alt.py +60 -0
  285. omlish/term/confirm.py +8 -8
  286. omlish/term/pager.py +235 -0
  287. omlish/term/terminfo.py +935 -0
  288. omlish/term/termstate.py +67 -0
  289. omlish/term/vt100/terminal.py +0 -3
  290. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  291. omlish/testing/pytest/plugins/skips.py +2 -5
  292. omlish/testing/unittest/main.py +3 -3
  293. omlish/text/docwrap/__init__.py +3 -0
  294. omlish/text/docwrap/__main__.py +11 -0
  295. omlish/text/docwrap/api.py +83 -0
  296. omlish/text/docwrap/cli.py +86 -0
  297. omlish/text/docwrap/groups.py +84 -0
  298. omlish/text/docwrap/lists.py +167 -0
  299. omlish/text/docwrap/parts.py +146 -0
  300. omlish/text/docwrap/reflowing.py +106 -0
  301. omlish/text/docwrap/rendering.py +151 -0
  302. omlish/text/docwrap/utils.py +11 -0
  303. omlish/text/docwrap/wrapping.py +59 -0
  304. omlish/text/filecache.py +2 -2
  305. omlish/text/lorem.py +6 -0
  306. omlish/text/parts.py +2 -2
  307. omlish/text/textwrap.py +51 -0
  308. omlish/typedvalues/__init__.py +1 -1
  309. omlish/typedvalues/marshal.py +85 -59
  310. omlish/typedvalues/values.py +2 -1
  311. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +36 -38
  312. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +323 -385
  313. omlish/asyncs/bluelet/LICENSE +0 -6
  314. omlish/asyncs/bluelet/all.py +0 -67
  315. omlish/asyncs/bluelet/api.py +0 -23
  316. omlish/asyncs/bluelet/core.py +0 -178
  317. omlish/asyncs/bluelet/events.py +0 -79
  318. omlish/asyncs/bluelet/files.py +0 -80
  319. omlish/asyncs/bluelet/runner.py +0 -417
  320. omlish/asyncs/bluelet/sockets.py +0 -216
  321. omlish/asyncs/bridge.py +0 -359
  322. omlish/asyncs/utils.py +0 -18
  323. omlish/dataclasses/impl/generation/mangling.py +0 -18
  324. omlish/defs.py +0 -216
  325. omlish/dispatch/_dispatch2.py +0 -69
  326. omlish/dispatch/_dispatch3.py +0 -108
  327. omlish/dynamic.py +0 -219
  328. omlish/formats/json/Json.g4 +0 -77
  329. omlish/formats/json/_antlr/JsonLexer.py +0 -109
  330. omlish/formats/json/_antlr/JsonListener.py +0 -61
  331. omlish/formats/json/_antlr/JsonParser.py +0 -457
  332. omlish/formats/json/_antlr/JsonVisitor.py +0 -42
  333. omlish/formats/json5/Json5.g4 +0 -168
  334. omlish/formats/json5/_antlr/Json5Lexer.py +0 -354
  335. omlish/formats/json5/_antlr/Json5Listener.py +0 -79
  336. omlish/formats/json5/_antlr/Json5Parser.py +0 -617
  337. omlish/formats/json5/_antlr/Json5Visitor.py +0 -52
  338. omlish/funcs/match.py +0 -227
  339. omlish/io/trampoline.py +0 -289
  340. omlish/lang/imports/proxyinit.py +0 -665
  341. omlish/lite/logs.py +0 -4
  342. omlish/lite/reprs.py +0 -85
  343. omlish/logs/abc.py +0 -319
  344. omlish/logs/callers.py +0 -67
  345. omlish/logs/color.py +0 -27
  346. omlish/logs/configs.py +0 -29
  347. omlish/logs/handlers.py +0 -17
  348. omlish/logs/protocol.py +0 -218
  349. omlish/logs/timing.py +0 -58
  350. omlish/marshal/factories/match.py +0 -34
  351. omlish/marshal/factories/simple.py +0 -28
  352. omlish/specs/irc/messages/base.py +0 -49
  353. omlish/specs/irc/messages/formats.py +0 -92
  354. omlish/specs/irc/messages/messages.py +0 -774
  355. omlish/specs/irc/messages/parsing.py +0 -98
  356. omlish/specs/irc/numerics/formats.py +0 -97
  357. omlish/specs/irc/numerics/numerics.py +0 -865
  358. omlish/specs/irc/numerics/types.py +0 -59
  359. omlish/specs/irc/protocol/LICENSE +0 -11
  360. omlish/specs/irc/protocol/__init__.py +0 -61
  361. omlish/specs/irc/protocol/consts.py +0 -6
  362. omlish/specs/irc/protocol/errors.py +0 -30
  363. omlish/specs/irc/protocol/message.py +0 -21
  364. omlish/specs/irc/protocol/nuh.py +0 -55
  365. omlish/specs/irc/protocol/parsing.py +0 -158
  366. omlish/specs/irc/protocol/rendering.py +0 -153
  367. omlish/specs/irc/protocol/tags.py +0 -102
  368. omlish/specs/irc/protocol/utils.py +0 -30
  369. omlish/specs/proto/Protobuf3.g4 +0 -396
  370. omlish/specs/proto/__init__.py +0 -0
  371. omlish/specs/proto/_antlr/Protobuf3Lexer.py +0 -340
  372. omlish/specs/proto/_antlr/Protobuf3Listener.py +0 -448
  373. omlish/specs/proto/_antlr/Protobuf3Parser.py +0 -3909
  374. omlish/specs/proto/_antlr/Protobuf3Visitor.py +0 -257
  375. omlish/specs/proto/_antlr/__init__.py +0 -0
  376. omlish/specs/proto/nodes.py +0 -54
  377. omlish/specs/proto/parsing.py +0 -97
  378. omlish/sql/parsing/Minisql.g4 +0 -292
  379. omlish/sql/parsing/__init__.py +0 -0
  380. omlish/sql/parsing/_antlr/MinisqlLexer.py +0 -322
  381. omlish/sql/parsing/_antlr/MinisqlListener.py +0 -511
  382. omlish/sql/parsing/_antlr/MinisqlParser.py +0 -3763
  383. omlish/sql/parsing/_antlr/MinisqlVisitor.py +0 -292
  384. omlish/sql/parsing/_antlr/__init__.py +0 -0
  385. omlish/sql/parsing/parsing.py +0 -119
  386. omlish/text/antlr/__init__.py +0 -3
  387. omlish/text/antlr/_runtime/BufferedTokenStream.py +0 -305
  388. omlish/text/antlr/_runtime/CommonTokenFactory.py +0 -64
  389. omlish/text/antlr/_runtime/CommonTokenStream.py +0 -90
  390. omlish/text/antlr/_runtime/FileStream.py +0 -30
  391. omlish/text/antlr/_runtime/InputStream.py +0 -90
  392. omlish/text/antlr/_runtime/IntervalSet.py +0 -183
  393. omlish/text/antlr/_runtime/LICENSE.txt +0 -28
  394. omlish/text/antlr/_runtime/LL1Analyzer.py +0 -176
  395. omlish/text/antlr/_runtime/Lexer.py +0 -332
  396. omlish/text/antlr/_runtime/ListTokenSource.py +0 -147
  397. omlish/text/antlr/_runtime/Parser.py +0 -583
  398. omlish/text/antlr/_runtime/ParserInterpreter.py +0 -173
  399. omlish/text/antlr/_runtime/ParserRuleContext.py +0 -189
  400. omlish/text/antlr/_runtime/PredictionContext.py +0 -632
  401. omlish/text/antlr/_runtime/Recognizer.py +0 -150
  402. omlish/text/antlr/_runtime/RuleContext.py +0 -230
  403. omlish/text/antlr/_runtime/StdinStream.py +0 -14
  404. omlish/text/antlr/_runtime/Token.py +0 -158
  405. omlish/text/antlr/_runtime/TokenStreamRewriter.py +0 -258
  406. omlish/text/antlr/_runtime/Utils.py +0 -36
  407. omlish/text/antlr/_runtime/__init__.py +0 -2
  408. omlish/text/antlr/_runtime/_all.py +0 -24
  409. omlish/text/antlr/_runtime/_pygrun.py +0 -174
  410. omlish/text/antlr/_runtime/atn/ATN.py +0 -135
  411. omlish/text/antlr/_runtime/atn/ATNConfig.py +0 -162
  412. omlish/text/antlr/_runtime/atn/ATNConfigSet.py +0 -215
  413. omlish/text/antlr/_runtime/atn/ATNDeserializationOptions.py +0 -27
  414. omlish/text/antlr/_runtime/atn/ATNDeserializer.py +0 -449
  415. omlish/text/antlr/_runtime/atn/ATNSimulator.py +0 -50
  416. omlish/text/antlr/_runtime/atn/ATNState.py +0 -267
  417. omlish/text/antlr/_runtime/atn/ATNType.py +0 -20
  418. omlish/text/antlr/_runtime/atn/LexerATNSimulator.py +0 -573
  419. omlish/text/antlr/_runtime/atn/LexerAction.py +0 -301
  420. omlish/text/antlr/_runtime/atn/LexerActionExecutor.py +0 -146
  421. omlish/text/antlr/_runtime/atn/ParserATNSimulator.py +0 -1664
  422. omlish/text/antlr/_runtime/atn/PredictionMode.py +0 -502
  423. omlish/text/antlr/_runtime/atn/SemanticContext.py +0 -333
  424. omlish/text/antlr/_runtime/atn/Transition.py +0 -271
  425. omlish/text/antlr/_runtime/atn/__init__.py +0 -4
  426. omlish/text/antlr/_runtime/dfa/DFA.py +0 -136
  427. omlish/text/antlr/_runtime/dfa/DFASerializer.py +0 -76
  428. omlish/text/antlr/_runtime/dfa/DFAState.py +0 -129
  429. omlish/text/antlr/_runtime/dfa/__init__.py +0 -4
  430. omlish/text/antlr/_runtime/error/DiagnosticErrorListener.py +0 -111
  431. omlish/text/antlr/_runtime/error/ErrorListener.py +0 -75
  432. omlish/text/antlr/_runtime/error/ErrorStrategy.py +0 -712
  433. omlish/text/antlr/_runtime/error/Errors.py +0 -176
  434. omlish/text/antlr/_runtime/error/__init__.py +0 -4
  435. omlish/text/antlr/_runtime/tree/Chunk.py +0 -33
  436. omlish/text/antlr/_runtime/tree/ParseTreeMatch.py +0 -121
  437. omlish/text/antlr/_runtime/tree/ParseTreePattern.py +0 -75
  438. omlish/text/antlr/_runtime/tree/ParseTreePatternMatcher.py +0 -377
  439. omlish/text/antlr/_runtime/tree/RuleTagToken.py +0 -53
  440. omlish/text/antlr/_runtime/tree/TokenTagToken.py +0 -50
  441. omlish/text/antlr/_runtime/tree/Tree.py +0 -194
  442. omlish/text/antlr/_runtime/tree/Trees.py +0 -114
  443. omlish/text/antlr/_runtime/tree/__init__.py +0 -2
  444. omlish/text/antlr/_runtime/xpath/XPath.py +0 -278
  445. omlish/text/antlr/_runtime/xpath/XPathLexer.py +0 -98
  446. omlish/text/antlr/_runtime/xpath/__init__.py +0 -4
  447. omlish/text/antlr/delimit.py +0 -109
  448. omlish/text/antlr/dot.py +0 -41
  449. omlish/text/antlr/errors.py +0 -14
  450. omlish/text/antlr/input.py +0 -96
  451. omlish/text/antlr/parsing.py +0 -54
  452. omlish/text/antlr/runtime.py +0 -102
  453. omlish/text/antlr/utils.py +0 -38
  454. /omlish/{asyncs/bluelet → cexts}/__init__.py +0 -0
  455. /omlish/{formats/json/_antlr → diag/cmds}/__init__.py +0 -0
  456. /omlish/{formats/json5/_antlr → http/clients/coro}/__init__.py +0 -0
  457. /omlish/{specs/irc → inject/helpers}/__init__.py +0 -0
  458. /omlish/{specs/irc/messages → logs/std}/__init__.py +0 -0
  459. /omlish/logs/{noisy.py → std/noisy.py} +0 -0
  460. /omlish/{specs/irc/numerics → marshal/composite/unions}/__init__.py +0 -0
  461. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
  462. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
  463. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
  464. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,4 @@
1
1
  import importlib.util
2
- import types
3
2
  import typing as ta
4
3
 
5
4
 
@@ -40,27 +39,3 @@ def lazy_import(
40
39
  return mod
41
40
 
42
41
  return inner
43
-
44
-
45
- def proxy_import(
46
- name: str,
47
- package: str | None = None,
48
- extras: ta.Iterable[str] | None = None,
49
- ) -> types.ModuleType:
50
- if isinstance(extras, str):
51
- raise TypeError(extras)
52
-
53
- omod = None
54
-
55
- def __getattr__(att): # noqa
56
- nonlocal omod
57
- if omod is None:
58
- omod = importlib.import_module(name, package=package)
59
- if extras:
60
- for x in extras:
61
- importlib.import_module(f'{name}.{x}', package=package)
62
- return getattr(omod, att)
63
-
64
- lmod = types.ModuleType(name)
65
- lmod.__getattr__ = __getattr__ # type: ignore[method-assign]
66
- return lmod
@@ -0,0 +1,559 @@
1
+ """
2
+ TODO:
3
+ - if already imported just return?
4
+ - no, need sub-imports..
5
+ - seal on first use? or just per module? can't seal roots and still be usable
6
+ - only if not hasattr?
7
+ - audit for deadlock risk - does importlib._bootstrap do it for us? do we need a global _ProxyImporter lock? would only
8
+ happen on reification
9
+ - ProxyImportError
10
+ - detect import reification in own module body - user is failing to properly 'hands-off' lazy import
11
+ - bonus points detect when done specifically for a type annotation
12
+
13
+ See:
14
+ - https://peps.python.org/pep-0810/
15
+ - https://github.com/LazyImportsCabal/cpython/tree/lazy
16
+ - https://developers.facebook.com/blog/post/2022/06/15/python-lazy-imports-with-cinder/
17
+ - https://engineering.fb.com/2024/01/18/developer-tools/lazy-imports-cinder-machine-learning-meta/
18
+ - https://www.hudsonrivertrading.com/hrtbeat/inside-hrts-python-fork/
19
+ - https://bugreports.qt.io/browse/PYSIDE-2404
20
+ - https://scientific-python.org/specs/spec-0001/
21
+ - https://github.com/scientific-python/lazy-loader
22
+ """
23
+ import functools
24
+ import importlib.util
25
+ import threading
26
+ import types
27
+ import typing as ta
28
+
29
+ from ..lazyglobals import LazyGlobals
30
+ from .capture import ImportCapture
31
+ from .capture import _new_import_capture_hook
32
+
33
+
34
+ _ProxyImporterModuleAttr: ta.TypeAlias = ta.Literal[
35
+ 'child', # 'outranks' proxy_attr - all child attrs must be proxy_attrs but not vice versa
36
+ 'proxy_attr',
37
+ 'pending_child',
38
+ 'pending_attr',
39
+ ]
40
+
41
+
42
+ ##
43
+
44
+
45
+ class _ProxyImporter:
46
+ def __init__(
47
+ self,
48
+ *,
49
+ owner_globals: ta.MutableMapping[str, ta.Any] | None = None,
50
+ ) -> None:
51
+ super().__init__()
52
+
53
+ self._owner_globals = owner_globals
54
+
55
+ self._owner_name: str | None = owner_globals.get('__name__') if owner_globals else None
56
+
57
+ # NOTE: Import machinery may be reentrant for things like gc ops and signal handling:
58
+ # TODO: audit for reentrancy this lol
59
+ # https://github.com/python/cpython/blob/72f25a8d9a5673d39c107cf522465a566b979ed5/Lib/importlib/_bootstrap.py#L233-L237 # noqa
60
+ self._lock = threading.RLock()
61
+
62
+ self._modules_by_name: dict[str, _ProxyImporter._Module] = {}
63
+ self._modules_by_proxy_obj: dict[types.ModuleType, _ProxyImporter._Module] = {}
64
+
65
+ class _Module:
66
+ def __init__(
67
+ self,
68
+ name: str,
69
+ getattr_handler: ta.Callable[['_ProxyImporter._Module', str], ta.Any],
70
+ *,
71
+ parent: ta.Optional['_ProxyImporter._Module'] = None,
72
+ ) -> None:
73
+ super().__init__()
74
+
75
+ self.name = name
76
+ self.parent = parent
77
+
78
+ self.base_name = name.rpartition('.')[2]
79
+ self.root: _ProxyImporter._Module = parent.root if parent is not None else self # noqa
80
+
81
+ self.children: dict[str, _ProxyImporter._Module] = {}
82
+ self.descendants: set[_ProxyImporter._Module] = set()
83
+
84
+ self.proxy_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {name}>')
85
+ self.proxy_obj.__file__ = None
86
+ self.proxy_obj.__getattr__ = functools.partial(getattr_handler, self) # type: ignore[method-assign]
87
+
88
+ self.pending_children: set[str] = set()
89
+ self.pending_attrs: set[str] = set()
90
+
91
+ real_obj: types.ModuleType | None = None
92
+
93
+ def __repr__(self) -> str:
94
+ return f'{self.__class__.__name__}<{self.name}{"!" if self.real_obj is not None else ""}>'
95
+
96
+ def find_attr(self, attr: str) -> _ProxyImporterModuleAttr | None:
97
+ is_child = attr in self.children
98
+ is_proxy_attr = attr in self.proxy_obj.__dict__
99
+ is_pending_child = attr in self.pending_children
100
+ is_pending_attr = attr in self.pending_attrs
101
+
102
+ if is_child:
103
+ if (
104
+ not is_proxy_attr or
105
+ is_pending_child or
106
+ is_pending_attr
107
+ ):
108
+ raise RuntimeError
109
+ return 'child'
110
+
111
+ elif is_proxy_attr:
112
+ if (
113
+ is_pending_child or
114
+ is_pending_attr
115
+ ):
116
+ raise RuntimeError
117
+ return 'proxy_attr'
118
+
119
+ elif is_pending_child:
120
+ if (
121
+ is_child or
122
+ is_proxy_attr or
123
+ is_pending_attr
124
+ ):
125
+ raise RuntimeError
126
+ return 'pending_child'
127
+
128
+ elif is_pending_attr:
129
+ if (
130
+ is_child or
131
+ is_proxy_attr or
132
+ is_pending_child
133
+ ):
134
+ raise RuntimeError
135
+ return 'pending_attr'
136
+
137
+ else:
138
+ return None
139
+
140
+ #
141
+
142
+ def _get_or_make_module_locked(self, name: str) -> _Module:
143
+ try:
144
+ return self._modules_by_name[name]
145
+ except KeyError:
146
+ pass
147
+
148
+ parent: _ProxyImporter._Module | None = None
149
+ if '.' in name:
150
+ rest, _, attr = name.rpartition('.')
151
+ parent = self._get_or_make_module_locked(rest)
152
+
153
+ fa = parent.find_attr(attr)
154
+ if not (fa == 'pending_child' or fa is None):
155
+ raise RuntimeError
156
+
157
+ if (ro := parent.real_obj) is not None and attr not in ro.__dict__:
158
+ raise NotImplementedError
159
+
160
+ module = self._modules_by_name[name] = _ProxyImporter._Module(
161
+ name,
162
+ self._handle_module_getattr,
163
+ )
164
+
165
+ self._modules_by_name[name] = module
166
+ self._modules_by_proxy_obj[module.proxy_obj] = module
167
+
168
+ if parent is not None:
169
+ parent.pending_children.discard(module.base_name)
170
+ parent.children[module.base_name] = module
171
+ setattr(parent.proxy_obj, module.base_name, module.proxy_obj)
172
+ parent.root.descendants.add(module)
173
+
174
+ return module
175
+
176
+ def _extend_module_locked(
177
+ self,
178
+ module: _Module,
179
+ *,
180
+ children: ta.Iterable[str] | None = None,
181
+ attrs: ta.Iterable[str] | None = None,
182
+ ) -> None:
183
+ for l in (children, attrs):
184
+ for n in l or ():
185
+ if n in module.proxy_obj.__dict__:
186
+ raise NotImplementedError
187
+
188
+ if (ro := module.real_obj) is not None and n in ro.__dict__:
189
+ raise NotImplementedError
190
+
191
+ for n in children or ():
192
+ fa = module.find_attr(n)
193
+ if not (fa == 'pending_child' or fa is None):
194
+ raise RuntimeError
195
+
196
+ for n in attrs or ():
197
+ fa = module.find_attr(n)
198
+ if not (fa == 'pending_attr' or fa is None):
199
+ raise RuntimeError
200
+
201
+ #
202
+
203
+ if children:
204
+ module.pending_children.update(n for n in children if n not in module.children)
205
+ if attrs:
206
+ module.pending_attrs.update(attrs)
207
+
208
+ def _retrieve_from_module_locked(self, module: _Module, attr: str) -> ta.Any:
209
+ fa = module.find_attr(attr)
210
+
211
+ if fa == 'child' or fa == 'proxy_attr':
212
+ return module.proxy_obj.__dict__[attr]
213
+
214
+ val: ta.Any
215
+
216
+ if fa == 'pending_child':
217
+ if module.name == self._owner_name:
218
+ val = importlib.import_module(f'{module.name}.{attr}')
219
+
220
+ else:
221
+ mod = __import__(
222
+ module.name,
223
+ self._owner_globals or {},
224
+ {},
225
+ [attr],
226
+ 0,
227
+ )
228
+
229
+ val = getattr(mod, attr)
230
+
231
+ module.pending_children.remove(attr)
232
+
233
+ elif fa == 'pending_attr' or fa is None:
234
+ if module.name == self._owner_name:
235
+ raise NotImplementedError
236
+
237
+ if (ro := module.real_obj) is None:
238
+ ro = module.real_obj = importlib.import_module(module.name)
239
+
240
+ val = getattr(ro, attr)
241
+
242
+ if fa == 'pending_attr':
243
+ module.pending_attrs.remove(attr)
244
+
245
+ else:
246
+ raise TypeError(fa)
247
+
248
+ setattr(module.proxy_obj, attr, val)
249
+ return val
250
+
251
+ #
252
+
253
+ def _handle_module_getattr(self, module: _Module, attr: str) -> ta.Any:
254
+ with self._lock:
255
+ return self._retrieve_from_module_locked(module, attr)
256
+
257
+ def add(
258
+ self,
259
+ module: str,
260
+ *,
261
+ children: ta.Iterable[str] | None = None,
262
+ attrs: ta.Iterable[str] | None = None,
263
+ ) -> types.ModuleType:
264
+ if isinstance(children, str):
265
+ raise TypeError(children)
266
+
267
+ # Leaf modules get collapsed into parents' pending_children
268
+ if not children and not attrs and '.' in module:
269
+ module, _, child = module.rpartition('.')
270
+ children = [child]
271
+
272
+ with self._lock:
273
+ m = self._get_or_make_module_locked(module)
274
+
275
+ if children or attrs:
276
+ self._extend_module_locked(
277
+ m,
278
+ children=children,
279
+ attrs=attrs,
280
+ )
281
+
282
+ return m.proxy_obj
283
+
284
+ def get_module(self, name: str) -> types.ModuleType:
285
+ try:
286
+ return self._modules_by_name[name].proxy_obj
287
+ except KeyError:
288
+ pass
289
+
290
+ with self._lock:
291
+ return self._get_or_make_module_locked(name).proxy_obj
292
+
293
+ def lookup(self, spec: str) -> ta.Any:
294
+ if '.' not in spec:
295
+ return self._modules_by_name[spec].proxy_obj
296
+
297
+ try:
298
+ module = self._modules_by_name[spec]
299
+ except KeyError:
300
+ pass
301
+ else:
302
+ return module.proxy_obj
303
+
304
+ rest, _, attr = spec.rpartition('.')
305
+ module = self._modules_by_name[rest]
306
+ return getattr(module.proxy_obj, attr)
307
+
308
+
309
+ #
310
+
311
+
312
+ _MODULE_PROXY_IMPORTER_GLOBAL_NAME = '__proxy_importer__'
313
+
314
+
315
+ def _get_module_proxy_importer(mod_globals: ta.MutableMapping[str, ta.Any]) -> _ProxyImporter:
316
+ """Assumed to only be called in a module body - no locking is done."""
317
+
318
+ pi: _ProxyImporter
319
+ try:
320
+ pi = mod_globals[_MODULE_PROXY_IMPORTER_GLOBAL_NAME]
321
+
322
+ except KeyError:
323
+ pi = _ProxyImporter(
324
+ owner_globals=mod_globals,
325
+ )
326
+ mod_globals[_MODULE_PROXY_IMPORTER_GLOBAL_NAME] = pi
327
+
328
+ else:
329
+ if pi.__class__ is not _ProxyImporter:
330
+ raise TypeError(pi)
331
+
332
+ if pi._owner_globals is not mod_globals: # noqa
333
+ raise RuntimeError
334
+
335
+ return pi
336
+
337
+
338
+ ##
339
+
340
+
341
+ def proxy_import(
342
+ spec: str,
343
+ package: str | None = None,
344
+ extras: ta.Iterable[str] | None = None,
345
+ *,
346
+ no_cache: bool = False,
347
+ ) -> types.ModuleType:
348
+ """'Legacy' proxy import mechanism."""
349
+
350
+ if isinstance(extras, str):
351
+ raise TypeError(extras)
352
+
353
+ omod = None
354
+
355
+ def __getattr__(att): # noqa
356
+ nonlocal omod
357
+
358
+ if omod is None:
359
+ omod = importlib.import_module(spec, package=package)
360
+ if extras:
361
+ for x in extras:
362
+ importlib.import_module(f'{spec}.{x}', package=package)
363
+
364
+ v = getattr(omod, att)
365
+
366
+ if not no_cache:
367
+ setattr(lmod, att, v)
368
+
369
+ return v
370
+
371
+ lmod = types.ModuleType(spec)
372
+ lmod.__getattr__ = __getattr__ # type: ignore[method-assign]
373
+ return lmod
374
+
375
+
376
+ #
377
+
378
+
379
+ def proxy_init(
380
+ init_globals: ta.MutableMapping[str, ta.Any],
381
+ spec: str,
382
+ attrs: ta.Iterable[str | tuple[str | None, str | None] | None] | None = None,
383
+ ) -> None:
384
+ name = importlib.util.resolve_name(
385
+ spec,
386
+ package=init_globals['__package__'] if spec.startswith('.') else None,
387
+ )
388
+
389
+ #
390
+
391
+ if isinstance(attrs, str):
392
+ raise TypeError(attrs)
393
+
394
+ if attrs is None:
395
+ attrs = [None]
396
+
397
+ whole_attr = spec.split('.')[-1]
398
+ al: list[tuple[str | None, str]] = []
399
+ for attr in attrs:
400
+ if attr is None:
401
+ al.append((None, whole_attr))
402
+
403
+ elif isinstance(attr, str):
404
+ al.append((attr, attr))
405
+
406
+ elif isinstance(attr, tuple):
407
+ imp_attr, as_attr = attr
408
+ if as_attr is None:
409
+ if imp_attr is None:
410
+ as_attr = whole_attr
411
+ else:
412
+ as_attr = imp_attr
413
+ al.append((imp_attr, as_attr))
414
+
415
+ else:
416
+ raise TypeError(attr)
417
+
418
+ #
419
+
420
+ pi = _get_module_proxy_importer(init_globals)
421
+ lg = LazyGlobals.install(init_globals)
422
+
423
+ pi.add(
424
+ name,
425
+ children=[r for l, r in al if r is not None],
426
+ )
427
+
428
+ for imp_attr, as_attr in al:
429
+ lg.set_fn(as_attr, functools.partial(pi.lookup, name if imp_attr is None else f'{name}.{imp_attr}'))
430
+
431
+
432
+ ##
433
+
434
+
435
+ class _AutoProxy:
436
+ def __init__(
437
+ self,
438
+ mod_globals: ta.MutableMapping[str, ta.Any],
439
+ *,
440
+ disable: bool = False,
441
+ eager: bool = False,
442
+
443
+ unreferenced_callback: ta.Callable[[ta.Sequence[str]], None] | None = None,
444
+ raise_unreferenced: bool = False,
445
+
446
+ update_exports: bool = False,
447
+
448
+ _stack_offset: int = 0,
449
+ _capture_impl: str | None = None,
450
+ ) -> None:
451
+ super().__init__()
452
+
453
+ self._mod_globals = mod_globals
454
+
455
+ self._disabled = disable
456
+ self._eager = eager
457
+
458
+ self._unreferenced_callback = unreferenced_callback
459
+ self._raise_unreferenced = raise_unreferenced
460
+
461
+ self._update_exports = update_exports
462
+
463
+ self._ic = ImportCapture(
464
+ mod_globals,
465
+ _hook=_new_import_capture_hook(
466
+ mod_globals,
467
+ stack_offset=_stack_offset + 1,
468
+ capture_impl=_capture_impl,
469
+ ),
470
+ disable=disable,
471
+ )
472
+ self._icc: ta.Any = None
473
+
474
+ def __enter__(self) -> ImportCapture:
475
+ if self._icc is not None:
476
+ raise RuntimeError
477
+
478
+ self._icc = self._ic.capture(
479
+ unreferenced_callback=self._unreferenced_callback,
480
+ raise_unreferenced=self._raise_unreferenced,
481
+ )
482
+
483
+ return self._icc.__enter__() # noqa
484
+
485
+ def __exit__(self, exc_type, exc_val, exc_tb):
486
+ if self._icc is None:
487
+ raise RuntimeError
488
+
489
+ self._icc.__exit__(exc_type, exc_val, exc_tb)
490
+
491
+ if not self._disabled and exc_type is None:
492
+ self._install()
493
+
494
+ # @abc.abstractmethod
495
+ def _install(self) -> None:
496
+ raise NotImplementedError
497
+
498
+
499
+ @ta.final
500
+ class _AutoProxyImport(_AutoProxy):
501
+ def _install(self) -> None:
502
+ cap = self._ic.captured
503
+
504
+ for cm in cap.modules.values():
505
+ if cm.attrs:
506
+ raise RuntimeError
507
+
508
+ pi = _get_module_proxy_importer(self._mod_globals)
509
+
510
+ for cm in cap.modules.values():
511
+ pi.add(
512
+ cm.name,
513
+ children=cm.children,
514
+ )
515
+
516
+ for ci in cap.imports.values():
517
+ pm = pi.get_module(ci.module.name)
518
+ for a in ci.as_ or ():
519
+ self._mod_globals[a] = pm
520
+
521
+ if self._eager:
522
+ for ci in cap.imports.values():
523
+ pi.lookup(ci.module.name)
524
+
525
+ if self._update_exports:
526
+ self._ic.update_exports()
527
+
528
+
529
+ @ta.final
530
+ class _AutoProxyInit(_AutoProxy):
531
+ def _install(self) -> None:
532
+ cap = self._ic.captured
533
+
534
+ pi = _get_module_proxy_importer(self._mod_globals)
535
+ lg = LazyGlobals.install(self._mod_globals)
536
+
537
+ for cm in cap.modules.values():
538
+ pi.add(
539
+ cm.name,
540
+ children=cm.children,
541
+ attrs=cm.attrs,
542
+ )
543
+
544
+ for ci in cap.imports.values():
545
+ for a in ci.as_ or ():
546
+ lg.set_fn(a, functools.partial(pi.lookup, ci.module.name))
547
+ for sa, da in ci.attrs or ():
548
+ lg.set_fn(da, functools.partial(pi.lookup, f'{ci.module.name}.{sa}'))
549
+
550
+ if self._eager:
551
+ for a in cap.attrs:
552
+ lg.get(a)
553
+
554
+ if self._update_exports:
555
+ self._ic.update_exports()
556
+
557
+
558
+ auto_proxy_import = _AutoProxyImport
559
+ auto_proxy_init = _AutoProxyInit
omlish/lang/iterables.py CHANGED
@@ -46,14 +46,14 @@ def chunk(n: int, iterable: ta.Iterable[T], strict: bool = False) -> ta.Iterator
46
46
  return iterator
47
47
 
48
48
 
49
- def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterable[T]:
49
+ def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterator[T]:
50
50
  for i, v in enumerate(vs):
51
51
  if i:
52
52
  yield d
53
53
  yield v
54
54
 
55
55
 
56
- def renumerate(it: ta.Iterable[T]) -> ta.Iterable[tuple[T, int]]:
56
+ def renumerate(it: ta.Iterable[T]) -> ta.Iterator[tuple[T, int]]:
57
57
  return ((e, i) for i, e in enumerate(it))
58
58
 
59
59
 
@@ -1,9 +1,25 @@
1
+ import threading
1
2
  import typing as ta
2
3
 
3
4
 
4
5
  ##
5
6
 
6
7
 
8
+ class AmbiguousLazyGlobalsFallbackError(Exception):
9
+ def __init__(self, attr: str, fallbacks: list[ta.Callable[[str], ta.Any]]) -> None:
10
+ super().__init__()
11
+
12
+ self.attr = attr
13
+ self.fallbacks = fallbacks
14
+
15
+ def __repr__(self) -> str:
16
+ return f'{self.__class__.__name__}({self.attr!r}, {self.fallbacks!r})'
17
+
18
+
19
+ _LAZY_GLOBALS_LOCK = threading.RLock()
20
+
21
+
22
+ @ta.final
7
23
  class LazyGlobals:
8
24
  def __init__(
9
25
  self,
@@ -11,12 +27,11 @@ class LazyGlobals:
11
27
  globals: ta.MutableMapping[str, ta.Any] | None = None, # noqa
12
28
  update_globals: bool = False,
13
29
  ) -> None:
14
- super().__init__()
15
-
16
30
  self._globals = globals
17
31
  self._update_globals = update_globals
18
32
 
19
33
  self._attr_fns: dict[str, ta.Callable[[], ta.Any]] = {}
34
+ self._fallback_fns: list[ta.Callable[[str], ta.Callable[[], ta.Any]]] = []
20
35
 
21
36
  @classmethod
22
37
  def install(cls, globals: ta.MutableMapping[str, ta.Any]) -> 'LazyGlobals': # noqa
@@ -25,30 +40,50 @@ class LazyGlobals:
25
40
  except KeyError:
26
41
  pass
27
42
  else:
28
- if not isinstance(xga, cls):
43
+ if xga.__class__ is not cls:
29
44
  raise RuntimeError(f'Module already has __getattr__ hook: {xga}') # noqa
30
45
  return xga
31
46
 
32
- lm = cls(
33
- globals=globals,
34
- update_globals=True,
35
- )
47
+ with _LAZY_GLOBALS_LOCK:
48
+ try:
49
+ xga = globals['__getattr__']
50
+ except KeyError:
51
+ pass
52
+ else:
53
+ if xga.__class__ is not cls:
54
+ raise RuntimeError(f'Module already has __getattr__ hook: {xga}') # noqa
55
+ return xga
36
56
 
37
- globals['__getattr__'] = lm
57
+ lm = cls(
58
+ globals=globals,
59
+ update_globals=True,
60
+ )
38
61
 
39
- return lm
62
+ globals['__getattr__'] = lm
63
+
64
+ return lm
40
65
 
41
66
  def set_fn(self, attr: str, fn: ta.Callable[[], ta.Any]) -> 'LazyGlobals':
42
67
  self._attr_fns[attr] = fn
43
68
  return self
44
69
 
70
+ def add_fallback_fn(self, fn: ta.Callable[[str], ta.Callable[[], ta.Any]]) -> 'LazyGlobals':
71
+ self._fallback_fns.append(fn)
72
+ return self
73
+
45
74
  def get(self, attr: str) -> ta.Any:
46
- try:
47
- fn = self._attr_fns[attr]
48
- except KeyError:
49
- raise AttributeError(attr) from None
75
+ val: ta.Any
76
+
77
+ if (attr_fn := self._attr_fns.get(attr)) is not None:
78
+ val = attr_fn()
50
79
 
51
- val = fn()
80
+ elif (fallbacks := [(fb_fn, fb_fn(attr)) for fb_fn in self._fallback_fns]):
81
+ if len(fallbacks) > 1:
82
+ raise AmbiguousLazyGlobalsFallbackError(attr, [fb_fn for fb_fn, _ in fallbacks])
83
+ [val] = fallbacks
84
+
85
+ else:
86
+ raise AttributeError(attr)
52
87
 
53
88
  if self._update_globals and self._globals is not None:
54
89
  self._globals[attr] = val