omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev493__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 (265) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/README.md +199 -0
  3. omlish/__about__.py +21 -16
  4. omlish/argparse/all.py +17 -9
  5. omlish/argparse/cli.py +16 -3
  6. omlish/argparse/utils.py +21 -0
  7. omlish/asyncs/asyncio/rlock.py +110 -0
  8. omlish/asyncs/asyncio/sync.py +43 -0
  9. omlish/asyncs/asyncio/utils.py +2 -0
  10. omlish/asyncs/sync.py +25 -0
  11. omlish/bootstrap/_marshal.py +1 -1
  12. omlish/bootstrap/diag.py +12 -21
  13. omlish/bootstrap/main.py +2 -5
  14. omlish/bootstrap/sys.py +27 -28
  15. omlish/cexts/__init__.py +0 -0
  16. omlish/cexts/include/omlish/omlish.hh +1 -0
  17. omlish/collections/__init__.py +13 -1
  18. omlish/collections/attrregistry.py +210 -0
  19. omlish/collections/cache/impl.py +1 -0
  20. omlish/collections/identity.py +1 -0
  21. omlish/collections/mappings.py +28 -0
  22. omlish/collections/trie.py +5 -1
  23. omlish/collections/utils.py +77 -0
  24. omlish/concurrent/all.py +2 -1
  25. omlish/concurrent/futures.py +25 -0
  26. omlish/concurrent/threadlets.py +1 -1
  27. omlish/daemons/reparent.py +2 -3
  28. omlish/daemons/spawning.py +2 -3
  29. omlish/dataclasses/__init__.py +2 -0
  30. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  31. omlish/dataclasses/impl/api/classes/make.py +3 -0
  32. omlish/dataclasses/impl/concerns/repr.py +15 -2
  33. omlish/dataclasses/impl/configs.py +97 -37
  34. omlish/dataclasses/impl/generation/compilation.py +21 -19
  35. omlish/dataclasses/impl/generation/globals.py +1 -0
  36. omlish/dataclasses/impl/generation/ops.py +1 -0
  37. omlish/dataclasses/impl/generation/plans.py +2 -17
  38. omlish/dataclasses/impl/generation/processor.py +106 -25
  39. omlish/dataclasses/impl/processing/base.py +8 -0
  40. omlish/dataclasses/impl/processing/driving.py +19 -7
  41. omlish/dataclasses/specs.py +1 -0
  42. omlish/dataclasses/tools/modifiers.py +5 -0
  43. omlish/diag/_pycharm/runhack.py +1 -1
  44. omlish/diag/cmds/__init__.py +0 -0
  45. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  46. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  47. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  48. omlish/diag/pycharm.py +16 -2
  49. omlish/diag/pydevd.py +58 -40
  50. omlish/diag/replserver/console.py +1 -1
  51. omlish/dispatch/__init__.py +18 -12
  52. omlish/dispatch/methods.py +50 -140
  53. omlish/dom/rendering.py +1 -1
  54. omlish/formats/dotenv.py +1 -1
  55. omlish/formats/json/stream/__init__.py +13 -0
  56. omlish/funcs/guard.py +225 -0
  57. omlish/graphs/dot/rendering.py +1 -1
  58. omlish/http/all.py +44 -4
  59. omlish/http/clients/asyncs.py +33 -27
  60. omlish/http/clients/base.py +17 -1
  61. omlish/http/clients/coro/__init__.py +0 -0
  62. omlish/http/clients/coro/sync.py +171 -0
  63. omlish/http/clients/default.py +208 -29
  64. omlish/http/clients/executor.py +56 -0
  65. omlish/http/clients/httpx.py +72 -11
  66. omlish/http/clients/middleware.py +181 -0
  67. omlish/http/clients/sync.py +33 -27
  68. omlish/http/clients/syncasync.py +49 -0
  69. omlish/http/clients/urllib.py +6 -3
  70. omlish/http/coro/client/connection.py +15 -6
  71. omlish/http/coro/io.py +2 -0
  72. omlish/http/flasky/__init__.py +40 -0
  73. omlish/http/flasky/_compat.py +2 -0
  74. omlish/http/flasky/api.py +82 -0
  75. omlish/http/flasky/app.py +203 -0
  76. omlish/http/flasky/cvs.py +59 -0
  77. omlish/http/flasky/requests.py +20 -0
  78. omlish/http/flasky/responses.py +23 -0
  79. omlish/http/flasky/routes.py +23 -0
  80. omlish/http/flasky/types.py +15 -0
  81. omlish/http/urls.py +67 -0
  82. omlish/inject/__init__.py +57 -29
  83. omlish/inject/_dataclasses.py +5148 -0
  84. omlish/inject/binder.py +11 -52
  85. omlish/inject/eagers.py +2 -0
  86. omlish/inject/elements.py +27 -0
  87. omlish/inject/helpers/__init__.py +0 -0
  88. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  89. omlish/inject/{tags.py → helpers/id.py} +2 -2
  90. omlish/inject/helpers/late.py +76 -0
  91. omlish/inject/{managed.py → helpers/managed.py} +10 -10
  92. omlish/inject/helpers/multis.py +143 -0
  93. omlish/inject/helpers/wrappers.py +54 -0
  94. omlish/inject/impl/elements.py +54 -21
  95. omlish/inject/impl/injector.py +29 -25
  96. omlish/inject/impl/inspect.py +10 -1
  97. omlish/inject/impl/maysync.py +3 -4
  98. omlish/inject/impl/multis.py +3 -0
  99. omlish/inject/impl/sync.py +3 -4
  100. omlish/inject/injector.py +31 -2
  101. omlish/inject/inspect.py +35 -0
  102. omlish/inject/maysync.py +2 -4
  103. omlish/inject/multis.py +8 -0
  104. omlish/inject/overrides.py +3 -3
  105. omlish/inject/privates.py +6 -0
  106. omlish/inject/providers.py +3 -2
  107. omlish/inject/sync.py +5 -4
  108. omlish/io/buffers.py +118 -0
  109. omlish/io/readers.py +29 -0
  110. omlish/iterators/transforms.py +2 -2
  111. omlish/lang/__init__.py +180 -97
  112. omlish/lang/_asyncs.cc +186 -0
  113. omlish/lang/asyncs.py +17 -0
  114. omlish/lang/casing.py +11 -0
  115. omlish/lang/contextmanagers.py +28 -4
  116. omlish/lang/functions.py +31 -22
  117. omlish/lang/imports/_capture.cc +11 -9
  118. omlish/lang/imports/capture.py +363 -170
  119. omlish/lang/imports/proxy.py +455 -152
  120. omlish/lang/lazyglobals.py +22 -9
  121. omlish/lang/params.py +17 -0
  122. omlish/lang/recursion.py +0 -1
  123. omlish/lang/sequences.py +124 -0
  124. omlish/lifecycles/README.md +30 -0
  125. omlish/lifecycles/__init__.py +87 -13
  126. omlish/lifecycles/_dataclasses.py +1388 -0
  127. omlish/lifecycles/base.py +178 -64
  128. omlish/lifecycles/contextmanagers.py +113 -4
  129. omlish/lifecycles/controller.py +150 -87
  130. omlish/lifecycles/injection.py +143 -0
  131. omlish/lifecycles/listeners.py +56 -0
  132. omlish/lifecycles/managed.py +142 -0
  133. omlish/lifecycles/manager.py +218 -93
  134. omlish/lifecycles/states.py +2 -0
  135. omlish/lifecycles/transitions.py +3 -0
  136. omlish/lifecycles/unwrap.py +57 -0
  137. omlish/lite/abstract.py +54 -24
  138. omlish/lite/asyncs.py +2 -2
  139. omlish/lite/attrops.py +2 -0
  140. omlish/lite/contextmanagers.py +4 -4
  141. omlish/lite/dataclasses.py +44 -0
  142. omlish/lite/maybes.py +8 -0
  143. omlish/lite/maysync.py +1 -5
  144. omlish/lite/pycharm.py +1 -1
  145. omlish/lite/typing.py +24 -0
  146. omlish/logs/_amalg.py +1 -1
  147. omlish/logs/all.py +25 -11
  148. omlish/logs/asyncs.py +73 -0
  149. omlish/logs/base.py +101 -12
  150. omlish/logs/contexts.py +4 -1
  151. omlish/logs/lists.py +125 -0
  152. omlish/logs/modules.py +19 -1
  153. omlish/logs/std/loggers.py +6 -1
  154. omlish/logs/std/noisy.py +11 -9
  155. omlish/logs/{standard.py → std/standard.py} +3 -4
  156. omlish/logs/utils.py +17 -2
  157. omlish/manifests/loading.py +2 -1
  158. omlish/marshal/__init__.py +33 -13
  159. omlish/marshal/_dataclasses.py +2774 -0
  160. omlish/marshal/base/configs.py +12 -0
  161. omlish/marshal/base/contexts.py +36 -21
  162. omlish/marshal/base/funcs.py +8 -11
  163. omlish/marshal/base/options.py +8 -0
  164. omlish/marshal/base/registries.py +146 -44
  165. omlish/marshal/base/types.py +40 -16
  166. omlish/marshal/composite/iterables.py +33 -20
  167. omlish/marshal/composite/literals.py +20 -18
  168. omlish/marshal/composite/mappings.py +36 -23
  169. omlish/marshal/composite/maybes.py +29 -19
  170. omlish/marshal/composite/newtypes.py +16 -16
  171. omlish/marshal/composite/optionals.py +14 -14
  172. omlish/marshal/composite/special.py +15 -15
  173. omlish/marshal/composite/unions/__init__.py +0 -0
  174. omlish/marshal/composite/unions/literals.py +93 -0
  175. omlish/marshal/composite/unions/primitives.py +103 -0
  176. omlish/marshal/factories/invalidate.py +18 -68
  177. omlish/marshal/factories/method.py +26 -0
  178. omlish/marshal/factories/moduleimport/factories.py +22 -65
  179. omlish/marshal/factories/multi.py +13 -25
  180. omlish/marshal/factories/recursive.py +42 -56
  181. omlish/marshal/factories/typecache.py +29 -74
  182. omlish/marshal/factories/typemap.py +42 -43
  183. omlish/marshal/objects/dataclasses.py +129 -106
  184. omlish/marshal/objects/marshal.py +18 -14
  185. omlish/marshal/objects/namedtuples.py +48 -42
  186. omlish/marshal/objects/unmarshal.py +19 -15
  187. omlish/marshal/polymorphism/marshal.py +9 -11
  188. omlish/marshal/polymorphism/metadata.py +16 -5
  189. omlish/marshal/polymorphism/standard.py +13 -1
  190. omlish/marshal/polymorphism/unions.py +15 -105
  191. omlish/marshal/polymorphism/unmarshal.py +9 -10
  192. omlish/marshal/singular/enums.py +14 -18
  193. omlish/marshal/standard.py +10 -6
  194. omlish/marshal/trivial/any.py +1 -1
  195. omlish/marshal/trivial/forbidden.py +21 -26
  196. omlish/metadata.py +23 -1
  197. omlish/os/forkhooks.py +4 -0
  198. omlish/os/pidfiles/pinning.py +2 -2
  199. omlish/reflect/__init__.py +43 -26
  200. omlish/reflect/ops.py +10 -1
  201. omlish/reflect/types.py +1 -0
  202. omlish/secrets/marshal.py +1 -1
  203. omlish/specs/jmespath/__init__.py +12 -3
  204. omlish/specs/jmespath/_dataclasses.py +2893 -0
  205. omlish/specs/jmespath/ast.py +1 -1
  206. omlish/specs/jsonrpc/__init__.py +13 -0
  207. omlish/specs/jsonrpc/_marshal.py +32 -23
  208. omlish/specs/jsonrpc/conns.py +10 -7
  209. omlish/specs/jsonschema/_marshal.py +1 -1
  210. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  211. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  212. omlish/specs/openapi/_marshal.py +31 -22
  213. omlish/sql/__init__.py +24 -5
  214. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  215. omlish/sql/api/dbapi.py +1 -1
  216. omlish/sql/dbapi/__init__.py +15 -0
  217. omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
  218. omlish/sql/queries/__init__.py +3 -0
  219. omlish/sql/queries/_marshal.py +2 -2
  220. omlish/sql/queries/rendering.py +1 -1
  221. omlish/sql/tabledefs/_marshal.py +1 -1
  222. omlish/subprocesses/base.py +4 -0
  223. omlish/subprocesses/editor.py +1 -1
  224. omlish/sync.py +155 -21
  225. omlish/term/alt.py +60 -0
  226. omlish/term/confirm.py +8 -8
  227. omlish/term/pager.py +235 -0
  228. omlish/term/terminfo.py +935 -0
  229. omlish/term/termstate.py +67 -0
  230. omlish/term/vt100/terminal.py +0 -3
  231. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  232. omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
  233. omlish/testing/pytest/plugins/skips.py +2 -1
  234. omlish/testing/unittest/main.py +3 -3
  235. omlish/text/docwrap/__init__.py +3 -0
  236. omlish/text/docwrap/__main__.py +11 -0
  237. omlish/text/docwrap/api.py +83 -0
  238. omlish/text/docwrap/cli.py +91 -0
  239. omlish/text/docwrap/groups.py +84 -0
  240. omlish/text/docwrap/lists.py +167 -0
  241. omlish/text/docwrap/parts.py +146 -0
  242. omlish/text/docwrap/reflowing.py +106 -0
  243. omlish/text/docwrap/rendering.py +151 -0
  244. omlish/text/docwrap/utils.py +11 -0
  245. omlish/text/docwrap/wrapping.py +59 -0
  246. omlish/text/filecache.py +2 -2
  247. omlish/text/lorem.py +6 -0
  248. omlish/text/parts.py +2 -2
  249. omlish/text/textwrap.py +51 -0
  250. omlish/typedvalues/marshal.py +85 -59
  251. omlish/typedvalues/values.py +2 -1
  252. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +36 -32
  253. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
  254. omlish/dataclasses/impl/generation/mangling.py +0 -18
  255. omlish/funcs/match.py +0 -227
  256. omlish/lifecycles/abstract.py +0 -86
  257. omlish/marshal/factories/match.py +0 -34
  258. omlish/marshal/factories/simple.py +0 -28
  259. /omlish/inject/{impl → helpers}/proxy.py +0 -0
  260. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  261. /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
  262. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
  263. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  264. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  265. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
@@ -359,5 +359,17 @@
359
359
  "module": "omlish.specs.jmespath.__main__"
360
360
  }
361
361
  }
362
+ },
363
+ {
364
+ "module": ".text.docwrap.__main__",
365
+ "attr": "_CLI_MODULE",
366
+ "file": "omlish/text/docwrap/__main__.py",
367
+ "line": 1,
368
+ "value": {
369
+ "!omdev.cli.types.CliModule": {
370
+ "name": "docwrap",
371
+ "module": "omlish.text.docwrap.__main__"
372
+ }
373
+ }
362
374
  }
363
375
  ]
omlish/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # Overview
2
+
3
+ Core utilities and foundational code. It's relatively large but completely self-contained, and has **no required
4
+ dependencies of any kind**.
5
+
6
+ # Notable packages
7
+
8
+ - **[lang](https://github.com/wrmsr/omlish/blob/master/omlish/lang)** - The standard library of this standard library.
9
+ Usually imported as a whole (`from omlish import lang`), it contains an array of general purpose utilities used
10
+ practically everywhere. It is kept relatively lightweight: its heaviest import is stdlib dataclasses and its
11
+ transitives. Some of its contents include:
12
+
13
+ - **[cached](https://github.com/wrmsr/omlish/blob/master/omlish/lang/cached)** - The standard `cached_function` /
14
+ `cached_property` tools, which are more capable than
15
+ [`functools.lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
16
+ - **[imports](https://github.com/wrmsr/omlish/blob/master/omlish/lang/imports.py)** - Import tools like:
17
+ - `proxy_import` - For late-loaded imports.
18
+ - `proxy_init` - For late-loaded module globals.
19
+ - `auto_proxy_init` - For automatic late-loaded package exports.
20
+ - **[classes](https://github.com/wrmsr/omlish/blob/master/omlish/lang/classes)** - Class tools and bases, such as
21
+ `Abstract` (which checks at subclass definition not instantiation), `Sealed` / `PackageSealed`, and `Final`.
22
+ - **[maybes](https://github.com/wrmsr/omlish/blob/master/omlish/lite/maybes.py)** - A simple, nestable formalization
23
+ of the presence or absence of an object, as in [many](https://en.cppreference.com/w/cpp/utility/optional)
24
+ [other](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
25
+ [languages](https://doc.rust-lang.org/std/option/).
26
+ - **[maysync](https://github.com/wrmsr/omlish/blob/master/omlish/lite/maysync.py)** - A lightweight means of sharing
27
+ code between sync and async contexts, eliminating the need for maintaining sync and async versions of functions.
28
+
29
+ - **[bootstrap](https://github.com/wrmsr/omlish/blob/master/omlish/bootstrap)** - A centralized, configurable,
30
+ all-in-one collection of various process-initialization minutiae like resource limiting, profiling, remote debugging,
31
+ log configuration, environment variables, et cetera. Usable as a context manager or via its
32
+ [cli](https://github.com/wrmsr/omlish/blob/master/omlish/bootstrap/main.py).
33
+
34
+ - **[collections](https://github.com/wrmsr/omlish/blob/master/omlish/collections)** - A handful of collection utilities
35
+ and simple implementations, including:
36
+
37
+ - **[cache](https://github.com/wrmsr/omlish/blob/master/omlish/collections/cache)** - A configurable LRU / LFU cache
38
+ with options like ttl and max size / weight.
39
+ - **[hasheq](https://github.com/wrmsr/omlish/blob/master/omlish/collections/hasheq.py)** - A dict taking an external
40
+ `__hash__` / `__eq__` implementation.
41
+ - **[identity](https://github.com/wrmsr/omlish/blob/master/omlish/collections/identity.py)** - Identity-keyed
42
+ collections.
43
+ - **[sorted](https://github.com/wrmsr/omlish/blob/master/omlish/collections/sorted)** - Interfaces for value-sorted
44
+ collections and key-sorted mappings, and a simple but correct skiplist-backed implementation.
45
+ - **[persistent](https://github.com/wrmsr/omlish/blob/master/omlish/collections/persistent)** - Interfaces for
46
+ [persistent](https://en.wikipedia.org/wiki/Persistent_data_structure) maps, and a simple but correct treap-backed
47
+ implementation.
48
+
49
+ - **[dataclasses](https://github.com/wrmsr/omlish/blob/master/omlish/dataclasses)** - A fully-compatible
50
+ reimplementation of stdlib [dataclasses](https://docs.python.org/3/library/dataclasses.html) with numerous
51
+ enhancements and additional features. The
52
+ [full stdlib test suite](https://github.com/wrmsr/omlish/blob/master/omlish/dataclasses/tests/cpython) is run against
53
+ it ensuring compatibility - they *are* dataclasses. Current enhancements include:
54
+
55
+ - Simple field coercion and validation.
56
+ - Any number of `@dc.init` or `@dc.validate` methods, not just a central `__post_init__`.
57
+ - Optional generic type parameter substitution in generated `__init__` methods, enabling accurate reflection.
58
+ - An optional [metaclass](https://github.com/wrmsr/omlish/blob/master/omlish/dataclasses/metaclass) which removes the
59
+ need for re-decorating subclasses (with support for inheritance of dataclass parameters like `frozen`), and some
60
+ basic [base classes](https://github.com/wrmsr/omlish/blob/master/omlish/dataclasses/metaclass/bases.py).
61
+ - Support for ahead-of-time / build-time code generation, significantly reducing import times.
62
+
63
+ The stdlib-equivalent api is exported in such a way as to appear to be direct aliases for the stdlib api itself,
64
+ simplifying tool support.
65
+
66
+ - **[dispatch](https://github.com/wrmsr/omlish/blob/master/omlish/dispatch)** - A beefed-up version of
67
+ [functools.singledispatch](https://docs.python.org/3/library/functools.html#functools.singledispatch), most notably
68
+ supporting MRO-honoring method impl dispatch.
69
+
70
+ - **[formats](https://github.com/wrmsr/omlish/blob/master/omlish/formats)** - Tools for various data formats, including:
71
+
72
+ - **[json](https://github.com/wrmsr/omlish/blob/master/omlish/formats/json)** - Tools for json, including abstraction
73
+ over various backends and a self-contained streaming / incremental parser.
74
+ - **[json5](https://github.com/wrmsr/omlish/blob/master/omlish/formats/json5)** - A self-contained and tested
75
+ [Json5](https://json5.org/) parser.
76
+ - **[toml](https://github.com/wrmsr/omlish/blob/master/omlish/formats/toml)** - Toml tools, including a
77
+ [lite](#lite-code) version of the stdlib parser (for use in older pythons).
78
+
79
+ - **[http](https://github.com/wrmsr/omlish/blob/master/omlish/http)** - HTTP code, including:
80
+
81
+ - **[clients](https://github.com/wrmsr/omlish/blob/master/omlish/http/clients)** - An abstraction over HTTP clients,
82
+ with urllib and httpx implementations.
83
+ - **[coro](https://github.com/wrmsr/omlish/blob/master/omlish/http/coro)** - Coroutine /
84
+ [sans-io](https://sans-io.readthedocs.io/) style reformulation of some stdlib http machinery - namely `http.server`
85
+ (and soon `http.client`). This style of code can run the same in sync, async, or
86
+ [any](https://docs.python.org/3/library/selectors.html)
87
+ [other](https://github.com/wrmsr/omlish/blob/master/omlish/asyncs/bluelet) context.
88
+
89
+ - **[inject](https://github.com/wrmsr/omlish/blob/master/omlish/inject)** - A
90
+ [guice](https://github.com/google/guice)-style dependency injector.
91
+
92
+ - **[io](https://github.com/wrmsr/omlish/blob/master/omlish/io)** - IO tools, including:
93
+
94
+ - **[compress](https://github.com/wrmsr/omlish/blob/master/omlish/io/compress)** - Abstraction over various
95
+ compression schemes, with particular attention to incremental operation. For example it includes
96
+ [an incremental reformulation of stdlib's gzip](https://github.com/wrmsr/omlish/blob/master/omlish/io/compress/gzip.py).
97
+ - **[coro](https://github.com/wrmsr/omlish/blob/master/omlish/io/coro)** - Utilities for coroutine / sans-io style
98
+ code.
99
+ - **[fdio](https://github.com/wrmsr/omlish/blob/master/omlish/io/fdio)** - An implementation of classic
100
+ [selector](https://docs.python.org/3/library/selectors.html)-style IO dispatch, akin to the deprecated
101
+ [asyncore](https://docs.python.org/3.11/library/asyncore.html). While more modern asyncio style code is generally
102
+ preferred, it nearly always involves
103
+ [background threads](https://github.com/python/cpython/blob/95d9dea1c4ed1b1de80074b74301cee0b38d5541/Lib/asyncio/unix_events.py#L1349)
104
+ making it [unsuitable for forking processes](https://rachelbythebay.com/w/2011/06/07/forked/) like
105
+ [process supervisors](https://github.com/wrmsr/omlish/blob/master/ominfra/supervisor).
106
+
107
+ - **[jmespath](https://github.com/wrmsr/omlish/blob/master/omlish/specs/jmespath)** - A vendoring of
108
+ [jmespath community edition](https://github.com/jmespath-community/python-jmespath), modernized and adapted to this
109
+ codebase.
110
+
111
+ - **[marshal](https://github.com/wrmsr/omlish/blob/master/omlish/marshal)** - A
112
+ [jackson](https://github.com/FasterXML/jackson)-style serde system.
113
+
114
+ - **[manifests](https://github.com/wrmsr/omlish/blob/master/omlish/manifests)** - A system for sharing lightweight
115
+ metadata within / across codebases.
116
+
117
+ - **[reflect](https://github.com/wrmsr/omlish/blob/master/omlish/reflect)** - Reflection utilities, including primarily
118
+ a formalization of stdlib type annotations for use at runtime, decoupled from stdlib impl detail. Keeping this working
119
+ is notoriously difficult across python versions (one of the primary reasons for only supporting 3.13+).
120
+
121
+ - **[sql](https://github.com/wrmsr/omlish/blob/master/omlish/sql)** - A collection of SQL utilities, including:
122
+
123
+ - **[api](https://github.com/wrmsr/omlish/blob/master/omlish/sql/api)** - An abstracted api for SQL interaction, with
124
+ support for dbapi compatible drivers (and a SQLAlchemy adapter).
125
+ - **[queries](https://github.com/wrmsr/omlish/blob/master/omlish/sql/queries)** - A SQL query builder with a fluent
126
+ interface.
127
+ - **[alchemy](https://github.com/wrmsr/omlish/blob/master/omlish/sql/alchemy)** - SQLAlchemy utilities. The codebase
128
+ has moved away from SQLAlchemy in favor of its own internal SQL api, but it will likely still remain as an optional
129
+ dep for the api adapter.
130
+
131
+ - **[testing](https://github.com/wrmsr/omlish/blob/master/omlish/testing)** - Test - primarily pytest - helpers,
132
+ including:
133
+
134
+ - **['harness'](https://github.com/wrmsr/omlish/blob/master/omlish/testing/pytest/inject/harness.py)** - An all-in-one
135
+ fixture marrying it to the codebase's dependency injector.
136
+ - **[plugins/async](https://github.com/wrmsr/omlish/blob/master/omlish/testing/pytest/plugins/asyncs)** - An in-house
137
+ async-backend abstraction plugin, capable of handling all of asyncio / trio / trio-asyncio /
138
+ *any-future-event-loop-impl* without having multiple fighting plugins (*[I know, I know](https://xkcd.com/927/)*).
139
+ - **[plugins](https://github.com/wrmsr/omlish/blob/master/omlish/testing/pytest/plugins)** - Various other plugins.
140
+
141
+ - **[typedvalues](https://github.com/wrmsr/omlish/blob/master/omlish/typedvalues)** - A little toolkit around 'boxed'
142
+ values, whose 'box' types convey more information than the bare values themselves. A rebellion against kwargs and env
143
+ vars: instead of `foo(bar=1, baz=2)`, you do `foo(Bar(1), Baz(2))`.
144
+
145
+ - **[lite](https://github.com/wrmsr/omlish/blob/master/omlish/lite)** - The standard library of 'lite' code. This is the
146
+ only package beneath `lang`, and parts of it are re-exported by it for deduplication. On top of miscellaneous
147
+ utilities it contains a handful of independent, self-contained, significantly simplified 'lite' equivalents of some
148
+ major core packages:
149
+
150
+ - **[lite/inject.py](https://github.com/wrmsr/omlish/blob/master/omlish/lite/inject.py)** - The lite injector, which
151
+ is more conservative with features and reflection than the core injector. The codebase's
152
+ [MiniGuice](https://github.com/google/guice/commit/70248eafa90cd70a68b293763e53f6aec656e73c).
153
+ - **[lite/marshal.py](https://github.com/wrmsr/omlish/blob/master/omlish/lite/marshal.py)** - The lite marshalling
154
+ system, which is a classic canned setup of simple type-specific 2-method classes and limited generic handling.
155
+
156
+ # Lite code
157
+
158
+ A subset of this codebase is written in a 'lite' style (non-'lite' code is referred to as *standard* code). While
159
+ standard code is written for python 3.13+, 'lite' code is written for 3.8+, and is written in a style conducive to
160
+ [amalgamation](https://github.com/wrmsr/omlish/blob/master/omdev#amalgamation) in which multiple python source files are
161
+ stitched together into one single self-contained python script.
162
+
163
+ Code written in this style has notable differences from standard code, including (but not limited to):
164
+
165
+ - No name mangling is done in amalgamation, which means (among other things) that code must be written expecting to be
166
+ all dumped into the same giant namespace. Where a standard class might be
167
+ [`omlish.inject.keys.Key`](https://github.com/wrmsr/omlish/blob/master/omlish/inject/keys.py), a lite equivalent might
168
+ be [`omlish.lite.inject.InjectorKey`](https://github.com/wrmsr/omlish/blob/master/omlish/lite/inject.py).
169
+ - All internal imports `import` each individual item out of modules rather than importing the modules and referencing
170
+ their contents. Where standard code would `from .. import x; x.y`, lite code would `from ..x import y; y`. As a result
171
+ there are frequently 'api' non-instantiated namespace classes serving the purpose of modules - just handy bags of
172
+ stuff with shortened names.
173
+ - As lite code is tested in 3.8+ but core code requires 3.13+, packages containing lite code can't import anything
174
+ standard in their (and their ancestors') `__init__.py`'s. Furthermore, `__init__.py` files are omitted outright in
175
+ amalgamation, so they effectively must be empty in any package containing any lite code. As a result there are
176
+ frequently [`all.py`](https://github.com/wrmsr/omlish/blob/master/omlish/configs/all.py) files in mixed-lite packages
177
+ which serve the purpose of `__init__.py` for standard usage - where importing standard packages from standard code
178
+ would be done via `from .. import lang`, importing mixed-lite packages from standard code would be done via
179
+ `from ..configs import all as cfgs`.
180
+
181
+ # Dependencies
182
+
183
+ This library has no required dependencies of any kind, but there are some optional integrations - see
184
+ [`__about__.py`](https://github.com/wrmsr/omlish/blob/master/omlish/__about__.py) for a full list, but some specific
185
+ examples are:
186
+
187
+ - **asttokens / executing** - For getting runtime source representations of function call arguments, an optional
188
+ capability of [check](https://github.com/wrmsr/omlish/blob/master/omlish/check.py).
189
+ - **anyio** - While lite code must use only asyncio, non-trivial async standard code prefers to be written to anyio.
190
+ - **pytest** - What is used for all standard testing - as lite code has no dependencies of any kind its testing uses
191
+ stdlib's [unittest](https://docs.python.org/3/library/unittest.html).
192
+ - **sqlalchemy** - The codebase has migrated away from SQLAlchemy in favor of the internal api but it retains it as an
193
+ optional dep to support adapting the internal api to it.
194
+
195
+ Additionally, some catchall dep categories include:
196
+
197
+ - **compression** - Various preferred compression backends like lz4, python-snappy, zstandard, and brotli.
198
+ - **formats** - Various preferred data format backends like orjson/ujson, pyyaml, cbor2, and cloudpickle.
199
+ - **sql drivers** - Various preferred and tested sql drivers.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev447'
2
- __revision__ = 'b7728880793e311f2b0080bb2b0ad2508b86e7b3'
1
+ __version__ = '0.0.0.dev493'
2
+ __revision__ = 'def7f860da290b32214a8db616442d7d9759fb58'
3
3
 
4
4
 
5
5
  #
@@ -36,12 +36,12 @@ class Project(ProjectBase):
36
36
 
37
37
  optional_dependencies = {
38
38
  'async': [
39
- 'anyio ~= 4.10',
39
+ 'anyio ~= 4.11',
40
40
  'sniffio ~= 1.3',
41
41
 
42
- 'greenlet ~= 3.2',
42
+ 'greenlet ~= 3.3',
43
43
 
44
- 'trio ~= 0.31',
44
+ 'trio ~= 0.32',
45
45
  'trio-asyncio ~= 0.15',
46
46
  ],
47
47
 
@@ -53,7 +53,7 @@ class Project(ProjectBase):
53
53
 
54
54
  'zstandard ~= 0.25; python_version < "3.14"',
55
55
 
56
- 'brotli ~= 1.1',
56
+ 'brotli ~= 1.2',
57
57
  ],
58
58
 
59
59
  'diag': [
@@ -79,11 +79,11 @@ class Project(ProjectBase):
79
79
  ],
80
80
 
81
81
  'misc': [
82
- 'wrapt ~= 1.17',
82
+ 'wrapt ~= 2.0',
83
83
  ],
84
84
 
85
85
  'secrets': [
86
- 'cryptography ~= 45.0',
86
+ 'cryptography ~= 46.0',
87
87
  ],
88
88
 
89
89
  'sqlalchemy': [
@@ -92,18 +92,18 @@ class Project(ProjectBase):
92
92
 
93
93
  'sqldrivers': [
94
94
  'pg8000 ~= 1.31',
95
- # 'psycopg2 ~= 2.9',
95
+ # 'psycopg2 ~= 3.3',
96
96
  # 'psycopg ~= 3.2',
97
97
 
98
98
  'pymysql ~= 1.1',
99
- # 'mysql-connector-python ~= 9.3',
99
+ # 'mysql-connector-python ~= 9.5',
100
100
  # 'mysqlclient ~= 2.2',
101
101
 
102
- 'aiomysql ~= 0.2',
103
- 'aiosqlite ~= 0.21',
104
- 'asyncpg ~= 0.30',
102
+ 'aiomysql ~= 0.3',
103
+ 'aiosqlite ~= 0.22',
104
+ 'asyncpg ~= 0.31',
105
105
 
106
- 'apsw ~= 3.50',
106
+ 'apsw ~= 3.51',
107
107
 
108
108
  'sqlean.py ~= 3.50',
109
109
 
@@ -117,7 +117,7 @@ class Project(ProjectBase):
117
117
  ],
118
118
 
119
119
  'testing': [
120
- 'pytest ~= 8.4',
120
+ 'pytest ~= 9.0',
121
121
  ],
122
122
  }
123
123
 
@@ -178,14 +178,19 @@ class SetuptoolsBase:
178
178
 
179
179
  '.omlish-manifests.json',
180
180
 
181
+ 'README',
182
+ 'README.md',
183
+
181
184
  'LICENSE',
182
185
  'LICENSE.txt',
186
+
187
+ 'AUTHORS',
183
188
  ],
184
189
  }
185
190
 
186
191
 
187
192
  class Setuptools(SetuptoolsBase):
188
- cexts = True
193
+ cext = True
189
194
 
190
195
  find_packages = {
191
196
  'include': [Project.name, f'{Project.name}.*'],
omlish/argparse/all.py CHANGED
@@ -1,17 +1,25 @@
1
1
  # ruff: noqa: I001
2
2
  import argparse
3
3
 
4
- from .cli import ( # noqa
5
- ArgparseArg as Arg,
6
- argparse_arg as arg,
7
- argparse_arg_ as arg_,
4
+ from .. import lang as _lang
8
5
 
9
- ArgparseCmdFn as CmdFn,
10
- ArgparseCmd as Cmd,
11
- argparse_cmd as cmd,
12
6
 
13
- ArgparseCli as Cli,
14
- )
7
+ with _lang.auto_proxy_init(globals()):
8
+ from .cli import ( # noqa
9
+ ArgparseArg as Arg,
10
+ argparse_arg as arg,
11
+ argparse_arg_ as arg_,
12
+
13
+ ArgparseCmdFn as CmdFn,
14
+ ArgparseCmd as Cmd,
15
+ argparse_cmd as cmd,
16
+
17
+ ArgparseCli as Cli,
18
+ )
19
+
20
+ from .utils import ( # noqa
21
+ NoExitArgumentParser,
22
+ )
15
23
 
16
24
 
17
25
  ##
omlish/argparse/cli.py CHANGED
@@ -10,6 +10,7 @@ TODO:
10
10
  - pre-run, post-run hooks
11
11
  - exitstack?
12
12
  - suggestion - difflib.get_close_matches
13
+ - add_argument_group - group kw on ArgparseKwarg?
13
14
  """
14
15
  import argparse
15
16
  import dataclasses as dc
@@ -36,6 +37,7 @@ ArgparseCmdFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
36
37
  class ArgparseArg:
37
38
  args: ta.Sequence[ta.Any]
38
39
  kwargs: ta.Mapping[str, ta.Any]
40
+ group: ta.Optional[str] = None
39
41
  dest: ta.Optional[str] = None
40
42
 
41
43
  def __get__(self, instance, owner=None):
@@ -45,7 +47,11 @@ class ArgparseArg:
45
47
 
46
48
 
47
49
  def argparse_arg(*args, **kwargs) -> ArgparseArg:
48
- return ArgparseArg(args, kwargs)
50
+ return ArgparseArg(
51
+ args=args,
52
+ group=kwargs.pop('group', None),
53
+ kwargs=kwargs,
54
+ )
49
55
 
50
56
 
51
57
  def argparse_arg_(*args, **kwargs) -> ta.Any:
@@ -215,6 +221,10 @@ class ArgparseCli:
215
221
  subparser.set_defaults(_cmd=obj)
216
222
 
217
223
  elif isinstance(obj, ArgparseArg):
224
+ if obj.group is not None:
225
+ # FIXME: add_argument_group
226
+ raise NotImplementedError
227
+
218
228
  if att in anns:
219
229
  ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
220
230
  obj.kwargs = {**ann_kwargs, **obj.kwargs}
@@ -260,7 +270,7 @@ class ArgparseCli:
260
270
 
261
271
  if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
262
272
  msg = f'unrecognized arguments: {" ".join(self._unknown_args)}'
263
- if (parser := self.get_parser()).exit_on_error:
273
+ if (parser := self.get_parser()).exit_on_error: # noqa
264
274
  parser.error(msg)
265
275
  else:
266
276
  raise argparse.ArgumentError(None, msg)
@@ -280,7 +290,10 @@ class ArgparseCli:
280
290
  return fn()
281
291
 
282
292
  def cli_run_and_exit(self) -> ta.NoReturn:
283
- sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
293
+ rc = self.cli_run()
294
+ if not isinstance(rc, int):
295
+ rc = 0
296
+ raise SystemExit(rc)
284
297
 
285
298
  def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
286
299
  if exit:
@@ -0,0 +1,21 @@
1
+ import argparse
2
+ import typing as ta
3
+
4
+
5
+ ##
6
+
7
+
8
+ class NoExitArgumentParser(argparse.ArgumentParser):
9
+ def __init__(
10
+ self,
11
+ *args: ta.Any,
12
+ exit_on_error: bool = False,
13
+ **kwargs: ta.Any,
14
+ ) -> None:
15
+ if exit_on_error:
16
+ raise TypeError('exit_on_error=True not supported')
17
+
18
+ super().__init__(*args, exit_on_error=False, **kwargs) # type: ignore[misc]
19
+
20
+ def exit(self, status=0, message=None):
21
+ raise TypeError('NoExitArgumentParser.exit() not supported')
@@ -0,0 +1,110 @@
1
+ # MIT License
2
+ #
3
+ # Copyright (c) 2023 Joshua George Albert
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ # https://github.com/Joshuaalbert/FairAsyncRLock/blob/7c3ed4e08892638dba262eaf0b1a7b1fad4b2608/fair_async_rlock/fair_async_rlock.py
19
+ import asyncio
20
+ import collections
21
+ import typing as ta
22
+
23
+
24
+ ##
25
+
26
+
27
+ class AsyncioRLock:
28
+ def __init__(self) -> None:
29
+ super().__init__()
30
+
31
+ self._owner: asyncio.Task | None = None
32
+ self._count = 0
33
+ self._owner_transfer = False
34
+ self._queue: collections.deque[asyncio.Event] = collections.deque()
35
+
36
+ def is_owner(self, task: asyncio.Task | None = None) -> bool:
37
+ if task is None:
38
+ task = asyncio.current_task()
39
+ return self._owner == task
40
+
41
+ def is_locked(self) -> bool:
42
+ return self._owner is not None
43
+
44
+ async def acquire(self) -> None:
45
+ me = asyncio.current_task()
46
+
47
+ # If the lock is reentrant, acquire it immediately
48
+ if self.is_owner(task=me):
49
+ self._count += 1
50
+ return
51
+
52
+ # If the lock is free (and ownership not in midst of transfer), acquire it immediately
53
+ if not self._count and not self._owner_transfer:
54
+ self._owner = me
55
+ self._count = 1
56
+ return
57
+
58
+ # Create an event for this task, to notify when it's ready for acquire
59
+ event = asyncio.Event()
60
+ self._queue.append(event)
61
+
62
+ # Wait for the lock to be free, then acquire
63
+ try:
64
+ await event.wait()
65
+ self._owner_transfer = False
66
+ self._owner = me
67
+ self._count = 1
68
+
69
+ except asyncio.CancelledError:
70
+ try: # if in queue, then cancelled before release
71
+ self._queue.remove(event)
72
+
73
+ except ValueError: # otherwise, release happened, this was next, and we simulate passing on
74
+ self._owner_transfer = False
75
+ self._owner = me
76
+ self._count = 1
77
+ self._current_task_release()
78
+
79
+ raise
80
+
81
+ def _current_task_release(self) -> None:
82
+ self._count -= 1
83
+ if not self._count:
84
+ self._owner = None
85
+
86
+ if self._queue:
87
+ # Wake up the next task in the queue
88
+ event = self._queue.popleft()
89
+ event.set()
90
+
91
+ # Setting this here prevents another task getting lock until owner transfer.
92
+ self._owner_transfer = True
93
+
94
+ def release(self) -> None:
95
+ me = asyncio.current_task()
96
+
97
+ if self._owner is None:
98
+ raise RuntimeError(f'Cannot release un-acquired lock. {me} tried to release.')
99
+
100
+ if not self.is_owner(task=me):
101
+ raise RuntimeError(f'Cannot release foreign lock. {me} tried to unlock {self._owner}.')
102
+
103
+ self._current_task_release()
104
+
105
+ async def __aenter__(self) -> ta.Self:
106
+ await self.acquire()
107
+ return self
108
+
109
+ async def __aexit__(self, exc_type, exc, tb):
110
+ self.release()
@@ -0,0 +1,43 @@
1
+ # ruff: noqa: UP045
2
+ # @omlish-lite
3
+ import asyncio
4
+ import typing as ta
5
+
6
+ from ...sync import SyncBufferRelay
7
+ from ..sync import AsyncBufferRelay
8
+
9
+
10
+ T = ta.TypeVar('T')
11
+
12
+
13
+ ##
14
+
15
+
16
+ @ta.final
17
+ class AsyncioBufferRelay(AsyncBufferRelay[T]):
18
+ def __init__(
19
+ self,
20
+ *,
21
+ event: ta.Optional[asyncio.Event] = None,
22
+ loop: ta.Optional[ta.Any] = None,
23
+ ) -> None:
24
+ if event is None:
25
+ event = asyncio.Event()
26
+ self._event = event
27
+ if loop is None:
28
+ loop = asyncio.get_running_loop()
29
+ self._loop = loop
30
+
31
+ self._relay: SyncBufferRelay[T] = SyncBufferRelay(
32
+ wake_fn=lambda: loop.call_soon_threadsafe(event.set), # type: ignore[arg-type]
33
+ )
34
+
35
+ def push(self, *vs: T) -> None:
36
+ self._relay.push(*vs)
37
+
38
+ def swap(self) -> ta.Sequence[T]:
39
+ return self._relay.swap()
40
+
41
+ async def wait(self) -> None:
42
+ await self._event.wait()
43
+ self._event.clear()
@@ -76,6 +76,8 @@ async def asyncio_wait_concurrent(
76
76
  if isinstance(concurrency, asyncio.Semaphore):
77
77
  semaphore = concurrency
78
78
  elif isinstance(concurrency, int):
79
+ if concurrency < 1:
80
+ raise ValueError(f'Concurrency must be >= 1, got {concurrency}')
79
81
  semaphore = asyncio.Semaphore(concurrency)
80
82
  else:
81
83
  raise TypeError(concurrency)
omlish/asyncs/sync.py ADDED
@@ -0,0 +1,25 @@
1
+ # @omlish-lite
2
+ import abc
3
+ import typing as ta
4
+
5
+ from ..lite.abstract import Abstract
6
+
7
+
8
+ T = ta.TypeVar('T')
9
+
10
+
11
+ ##
12
+
13
+
14
+ class AsyncBufferRelay(Abstract, ta.Generic[T]):
15
+ @abc.abstractmethod
16
+ def push(self, *vs: T) -> None:
17
+ raise NotImplementedError
18
+
19
+ @abc.abstractmethod
20
+ def swap(self) -> ta.Sequence[T]:
21
+ raise NotImplementedError
22
+
23
+ @abc.abstractmethod
24
+ async def wait(self) -> None:
25
+ raise NotImplementedError
@@ -8,7 +8,7 @@ from .harness import BOOTSTRAP_TYPES_BY_NAME
8
8
 
9
9
 
10
10
  @lang.static_init
11
- def _install_standard_marshalling() -> None:
11
+ def _install_standard_marshaling() -> None:
12
12
  cfgs_poly = msh.Polymorphism(
13
13
  Bootstrap.Config,
14
14
  [msh.Impl(b.Config, n) for n, b in BOOTSTRAP_TYPES_BY_NAME.items()],