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
@@ -0,0 +1,82 @@
1
+ import threading
2
+ import typing as ta
3
+
4
+ from ... import lang
5
+ from ._compat import compat
6
+ from .app import App
7
+ from .cvs import Cvs
8
+ from .requests import Request
9
+ from .responses import Response
10
+
11
+
12
+ ##
13
+
14
+
15
+ class Api:
16
+ def __init__(
17
+ self,
18
+ *,
19
+ base_app_cls: type[App] = App,
20
+ ) -> None:
21
+ super().__init__()
22
+
23
+ self._base_app_cls = base_app_cls
24
+
25
+ self._lock = threading.RLock()
26
+
27
+ @property
28
+ def _cls_name_repr(self) -> str:
29
+ return f'{self.__class__.__name__}@{id(self):x}'
30
+
31
+ def __repr__(self) -> str:
32
+ return f'{self._cls_name_repr}()'
33
+
34
+ ##
35
+ # app cls
36
+
37
+ _app_cls: type[App]
38
+
39
+ @property
40
+ def app_cls(self) -> type[App]:
41
+ try:
42
+ return self._app_cls
43
+ except AttributeError:
44
+ pass
45
+
46
+ with self._lock:
47
+ try:
48
+ return self._app_cls
49
+ except AttributeError:
50
+ pass
51
+
52
+ self._app_cls = lang.new_type( # noqa
53
+ f'{self._base_app_cls.__name__}<{self._cls_name_repr}>',
54
+ (self._base_app_cls,),
55
+ {'_api': self},
56
+ )
57
+
58
+ return self._app_cls
59
+
60
+ @property
61
+ @compat
62
+ def Flask(self) -> type[App]: # noqa
63
+ return self.app_cls
64
+
65
+ ##
66
+ # helpers
67
+
68
+ def abort(self, code: int | Response, *args: ta.Any, **kwargs: ta.Any) -> ta.NoReturn:
69
+ raise NotImplementedError
70
+
71
+ ##
72
+ # cv's
73
+
74
+ @property
75
+ @compat
76
+ def request(self) -> Request:
77
+ return Cvs.REQUEST.get()
78
+
79
+ ##
80
+ # type aliases - must be last
81
+
82
+ Response = Response
@@ -0,0 +1,203 @@
1
+ import typing as ta
2
+
3
+ from ... import check
4
+ from ... import dataclasses as dc
5
+ from ._compat import compat
6
+ from .cvs import Cvs
7
+ from .responses import Response
8
+ from .routes import Route
9
+ from .routes import RouteKey
10
+
11
+
12
+ if ta.TYPE_CHECKING:
13
+ from .api import Api
14
+
15
+
16
+ T = ta.TypeVar('T')
17
+
18
+
19
+ ##
20
+
21
+
22
+ @dc.dataclass(frozen=True, kw_only=True)
23
+ class AppRunParams:
24
+ app: 'App'
25
+
26
+ port: int | None = None
27
+
28
+
29
+ AppRunner: ta.TypeAlias = ta.Callable[[AppRunParams], None]
30
+
31
+
32
+ ##
33
+
34
+
35
+ ViewFunc: ta.TypeAlias = ta.Callable[[], str]
36
+
37
+ BeforeRequestFunc: ta.TypeAlias = ta.Callable[[], Response | None]
38
+ AfterRequestFunc: ta.TypeAlias = ta.Callable[[ta.Any], ta.Any]
39
+
40
+
41
+ #
42
+
43
+
44
+ class App:
45
+ _api: ta.ClassVar['Api']
46
+
47
+ def __init__(
48
+ self,
49
+ import_name: str,
50
+ ) -> None:
51
+ check.not_none(self._api)
52
+
53
+ super().__init__()
54
+
55
+ self._import_name = check.non_empty_str(import_name)
56
+
57
+ self._routes: set[Route] = set()
58
+ self._routes_by_key: dict[RouteKey, Route] = {}
59
+
60
+ self._view_funcs_by_endpoint: dict[str, ViewFunc] = {}
61
+
62
+ self._before_request_funcs: list[BeforeRequestFunc] = []
63
+ self._after_request_funcs: list[AfterRequestFunc] = []
64
+
65
+ def __repr__(self) -> str:
66
+ return f'{self.__class__.__name__}@{id(self):x}({self._import_name!r})'
67
+
68
+ @property
69
+ def import_name(self) -> str:
70
+ return self._import_name
71
+
72
+ ##
73
+ # routing
74
+
75
+ @property
76
+ def routes(self) -> ta.AbstractSet[Route]:
77
+ return self._routes
78
+
79
+ @property
80
+ def routes_by_key(self) -> ta.Mapping[RouteKey, Route]:
81
+ return self._routes_by_key
82
+
83
+ @property
84
+ def view_funcs_by_endpoint(self) -> ta.Mapping[str, ViewFunc]:
85
+ return self._view_funcs_by_endpoint
86
+
87
+ @compat
88
+ def get(self, rule: str, **options: ta.Any) -> ta.Callable[[T], T]:
89
+ return self._method_route('GET', rule, **options)
90
+
91
+ @compat
92
+ def post(self, rule: str, **options: ta.Any) -> ta.Callable[[T], T]:
93
+ return self._method_route('POST', rule, **options)
94
+
95
+ @compat
96
+ def put(self, rule: str, **options: ta.Any) -> ta.Callable[[T], T]:
97
+ return self._method_route('PUT', rule, **options)
98
+
99
+ @compat
100
+ def delete(self, rule: str, **options: ta.Any) -> ta.Callable[[T], T]:
101
+ return self._method_route('DELETE', rule, **options)
102
+
103
+ @compat
104
+ def patch(self, rule: str, **options: ta.Any) -> ta.Callable[[T], T]:
105
+ return self._method_route('PATCH', rule, **options)
106
+
107
+ def _method_route(
108
+ self,
109
+ method: str,
110
+ rule: str,
111
+ **options: ta.Any,
112
+ ) -> ta.Callable[[T], T]:
113
+ return self.route(
114
+ rule,
115
+ methods=[method],
116
+ **options,
117
+ )
118
+
119
+ @compat
120
+ def route(
121
+ self,
122
+ rule: str,
123
+ **options: ta.Any,
124
+ ) -> ta.Callable[[T], T]:
125
+ def inner(fn):
126
+ endpoint = options.pop('endpoint', None)
127
+ self.add_url_rule(rule, endpoint, fn, **options)
128
+ return fn
129
+
130
+ return inner
131
+
132
+ @compat
133
+ def add_url_rule(
134
+ self,
135
+ rule: str,
136
+ endpoint: str | None = None,
137
+ view_func: ViewFunc | None = None,
138
+ *,
139
+ methods: ta.Iterable[str] | None = None,
140
+ ) -> None:
141
+ check.arg(check.non_empty_str(rule).startswith('/'))
142
+
143
+ if endpoint is None:
144
+ endpoint = check.not_none(view_func).__name__
145
+
146
+ if methods is None:
147
+ methods = ['GET']
148
+ else:
149
+ methods = [check.non_empty_str(m) for m in check.not_isinstance(methods, str)]
150
+ methods = check.not_empty([s.upper() for s in methods])
151
+
152
+ #
153
+
154
+ route = Route(
155
+ rule=rule,
156
+ endpoint=endpoint,
157
+ methods=frozenset(methods),
158
+ )
159
+
160
+ #
161
+
162
+ for rk in route.keys:
163
+ check.not_in(rk, self._routes_by_key)
164
+
165
+ if view_func is not None:
166
+ check.not_in(endpoint, self._view_funcs_by_endpoint)
167
+
168
+ #
169
+
170
+ self._routes.add(route)
171
+ for rk in route.keys:
172
+ self._routes_by_key[rk] = route
173
+
174
+ if view_func is not None:
175
+ self._view_funcs_by_endpoint[endpoint] = view_func
176
+
177
+ ##
178
+ # hooks
179
+
180
+ @property
181
+ def before_request_funcs(self) -> ta.Sequence[BeforeRequestFunc]:
182
+ return self._before_request_funcs
183
+
184
+ @property
185
+ def after_request_funcs(self) -> ta.Sequence[AfterRequestFunc]:
186
+ return self._after_request_funcs
187
+
188
+ def before_request(self, func: T) -> T: # BeforeRequestFunc
189
+ self._before_request_funcs.append(ta.cast(BeforeRequestFunc, check.callable(func)))
190
+ return func
191
+
192
+ def after_request(self, func: T) -> T: # AfterRequestFunc
193
+ self._after_request_funcs.append(ta.cast(AfterRequestFunc, check.callable(func)))
194
+ return func
195
+
196
+ ##
197
+ # running
198
+
199
+ @compat
200
+ def run(self, **kwargs: ta.Any) -> None:
201
+ params = AppRunParams(app=self, **kwargs)
202
+ runner = Cvs.APP_RUNNER.get()
203
+ check.not_none(runner)(params)
@@ -0,0 +1,59 @@
1
+ import contextlib
2
+ import contextvars
3
+ import typing as ta
4
+
5
+ from ... import dataclasses as dc
6
+ from ... import lang
7
+
8
+
9
+ if ta.TYPE_CHECKING:
10
+ from .app import AppRunner
11
+ from .requests import Request
12
+
13
+
14
+ T = ta.TypeVar('T')
15
+
16
+
17
+ ##
18
+
19
+
20
+ @dc.dataclass()
21
+ class CvLookupError(LookupError):
22
+ cv: 'Cv'
23
+
24
+
25
+ @ta.final
26
+ class Cv(ta.Generic[T]):
27
+ def __init__(self, name: str) -> None:
28
+ self._name = name
29
+ self._var: contextvars.ContextVar[T] = contextvars.ContextVar(name)
30
+
31
+ def __repr__(self) -> str:
32
+ return f'{self.__class__.__name__}({self._name!r})'
33
+
34
+ def get(self) -> T:
35
+ try:
36
+ return self._var.get()
37
+ except LookupError:
38
+ raise CvLookupError(self) from None
39
+
40
+ @contextlib.contextmanager
41
+ def set(self, new: T) -> ta.Iterator[None]:
42
+ tok = self._var.set(new)
43
+ try:
44
+ yield
45
+ finally:
46
+ self._var.reset(tok)
47
+
48
+
49
+ ##
50
+
51
+
52
+ def _cv(name: str) -> Cv:
53
+ return Cv(f'{__name__}.Cvs.{name}')
54
+
55
+
56
+ class Cvs(lang.Namespace, lang.Final):
57
+ APP_RUNNER: Cv['AppRunner'] = _cv('APP_RUNNER')
58
+
59
+ REQUEST: Cv['Request'] = _cv('REQUEST')
@@ -0,0 +1,20 @@
1
+ import typing as ta
2
+
3
+ from ... import dataclasses as dc
4
+ from ._compat import compat
5
+ from .types import ImmutableMultiDict
6
+
7
+
8
+ ##
9
+
10
+
11
+ @compat
12
+ @dc.dataclass(frozen=True, kw_only=True)
13
+ class Request:
14
+ path: str
15
+ method: str
16
+
17
+ form: ImmutableMultiDict[str, str] | None = None
18
+
19
+ def get_json(self) -> ta.Any:
20
+ raise NotImplementedError
@@ -0,0 +1,23 @@
1
+ import http
2
+ import typing as ta
3
+
4
+ from ... import dataclasses as dc
5
+ from ._compat import compat
6
+
7
+
8
+ ResponseData: ta.TypeAlias = str | ta.Iterable[str] | bytes | ta.Iterable[bytes]
9
+ ResponseStatus: ta.TypeAlias = int | str | http.HTTPStatus
10
+ ResponseHeaders: ta.TypeAlias = ta.Mapping[str, str | ta.Iterable[str]] | ta.Iterable[tuple[str, str]]
11
+
12
+
13
+ ##
14
+
15
+
16
+ @compat
17
+ @dc.dataclass(frozen=True)
18
+ class Response:
19
+ response: ResponseData | None = None
20
+ status: ResponseStatus | None = None
21
+ headers: ResponseHeaders | None = None
22
+ mimetype: str | None = None
23
+ content_type: str | None = None
@@ -0,0 +1,23 @@
1
+ import typing as ta
2
+
3
+ from ... import cached
4
+ from ... import dataclasses as dc
5
+
6
+
7
+ ##
8
+
9
+
10
+ class RouteKey(ta.NamedTuple):
11
+ rule: str
12
+ method: str
13
+
14
+
15
+ @dc.dataclass(frozen=True, kw_only=True)
16
+ class Route:
17
+ rule: str
18
+ endpoint: str
19
+ methods: frozenset[str]
20
+
21
+ @cached.property
22
+ def keys(self) -> frozenset[RouteKey]:
23
+ return frozenset(RouteKey(self.rule, m) for m in self.methods)
@@ -0,0 +1,15 @@
1
+ import typing as ta
2
+
3
+ from ._compat import compat
4
+
5
+
6
+ K = ta.TypeVar('K')
7
+ V = ta.TypeVar('V')
8
+
9
+
10
+ ##
11
+
12
+
13
+ @compat
14
+ class ImmutableMultiDict(ta.Mapping[K, V]):
15
+ pass
omlish/http/urls.py ADDED
@@ -0,0 +1,67 @@
1
+ # ruff: noqa: UP006 UP007 UP045
2
+ # @omlish-lite
3
+ import re
4
+ import typing as ta
5
+ import urllib.parse
6
+
7
+ from ..lite.cached import cached_nullary
8
+
9
+
10
+ ##
11
+
12
+
13
+ @cached_nullary
14
+ def _url_split_host_pat() -> re.Pattern:
15
+ return re.compile('//([^/#?]*)(.*)', re.DOTALL)
16
+
17
+
18
+ def url_split_host(url: str) -> ta.Tuple[ta.Optional[str], str]:
19
+ """splithost('//host[:port]/path') --> 'host[:port]', '/path'."""
20
+
21
+ # https://github.com/python/cpython/blob/364ae607d8035db8ba92486ebebd8225446c1a90/Lib/urllib/parse.py#L1143
22
+ if not (m := _url_split_host_pat().match(url)):
23
+ return None, url
24
+
25
+ host_port, path = m.groups()
26
+ if path and path[0] != '/':
27
+ path = '/' + path
28
+ return host_port, path
29
+
30
+
31
+ ##
32
+
33
+
34
+ def unparse_url_request_path(url: ta.Union[str, urllib.parse.ParseResult]) -> str:
35
+ if isinstance(url, urllib.parse.ParseResult):
36
+ ups = url
37
+ else:
38
+ ups = urllib.parse.urlparse(url)
39
+
40
+ return urllib.parse.urlunparse((
41
+ '',
42
+ '',
43
+ ups.path,
44
+ ups.params,
45
+ ups.query,
46
+ ups.fragment,
47
+ ))
48
+
49
+
50
+ def parsed_url_replace(
51
+ url: urllib.parse.ParseResult,
52
+ *,
53
+ scheme: ta.Optional[str] = None,
54
+ netloc: ta.Optional[str] = None,
55
+ path: ta.Optional[str] = None,
56
+ params: ta.Optional[str] = None,
57
+ query: ta.Optional[str] = None,
58
+ fragment: ta.Optional[str] = None,
59
+ ) -> urllib.parse.ParseResult:
60
+ return urllib.parse.ParseResult(
61
+ scheme if scheme is not None else url.scheme,
62
+ netloc if netloc is not None else url.netloc,
63
+ path if path is not None else url.path,
64
+ params if params is not None else url.params,
65
+ query if query is not None else url.query,
66
+ fragment if fragment is not None else url.fragment,
67
+ )
omlish/inject/__init__.py CHANGED
@@ -19,11 +19,49 @@ from .. import lang as _lang # noqa
19
19
  with _lang.auto_proxy_init(globals()):
20
20
  ##
21
21
 
22
+ from .helpers.constfn import ( # noqa
23
+ ConstFn,
24
+ )
25
+
26
+ from .helpers.id import ( # noqa
27
+ Id,
28
+ )
29
+
30
+ from .helpers.late import ( # noqa
31
+ Late,
32
+ AsyncLate,
33
+
34
+ bind_late,
35
+ bind_async_late,
36
+ )
37
+
38
+ from .helpers.managed import ( # noqa
39
+ create_async_managed_injector,
40
+ make_async_managed_provider,
41
+
42
+ create_managed_injector,
43
+ make_managed_provider,
44
+
45
+ create_maysync_managed_injector,
46
+ make_maysync_managed_provider,
47
+ )
48
+
49
+ from .helpers.multis import ( # noqa
50
+ bind_map_entry_const,
51
+ bind_set_entry_const,
52
+
53
+ ItemsBinderHelper,
54
+ items_binder_helper,
55
+ )
56
+
57
+ from .helpers.wrappers import ( # noqa
58
+ WrapperBinderHelper,
59
+ wrapper_binder_helper,
60
+ )
61
+
22
62
  from .binder import ( # noqa
23
63
  bind,
24
64
  bind_as_fn,
25
- bind_map_entry_const,
26
- bind_set_entry_const,
27
65
  )
28
66
 
29
67
  from .bindings import ( # noqa
@@ -36,9 +74,14 @@ with _lang.auto_proxy_init(globals()):
36
74
 
37
75
  from .elements import ( # noqa
38
76
  Element,
39
- Elemental,
40
77
  Elements,
78
+ Elemental,
79
+
41
80
  as_elements,
81
+ iter_elements,
82
+
83
+ CollectedElements,
84
+ collect_elements,
42
85
  )
43
86
 
44
87
  from .errors import ( # noqa
@@ -74,32 +117,25 @@ with _lang.auto_proxy_init(globals()):
74
117
  bind_provision_listener,
75
118
  )
76
119
 
77
- from .managed import ( # noqa
78
- create_async_managed_injector,
79
- make_async_managed_provider,
80
-
81
- create_managed_injector,
82
- make_managed_provider,
83
-
84
- create_maysync_managed_injector,
85
- make_maysync_managed_provider,
86
- )
87
-
88
120
  from .maysync import ( # noqa
89
121
  MaysyncInjector,
90
122
  create_maysync_injector,
91
123
  )
92
124
 
93
125
  from .multis import ( # noqa
94
- MapBinding,
95
- MapProvider,
126
+ is_set_multi_key,
96
127
  SetBinding,
97
128
  SetProvider,
98
- MapBinder,
129
+
130
+ is_map_multi_key,
131
+ MapBinding,
132
+ MapProvider,
133
+
99
134
  SetBinder,
135
+ set_binder,
100
136
 
101
- MapBinder as map_binder, # noqa
102
- SetBinder as set_binder, # noqa
137
+ MapBinder,
138
+ map_binder,
103
139
  )
104
140
 
105
141
 
@@ -116,10 +152,10 @@ with _lang.auto_proxy_init(globals()):
116
152
 
117
153
  from .privates import ( # noqa
118
154
  Expose,
155
+ expose,
156
+
119
157
  Private,
120
158
  private,
121
-
122
- Expose as expose, # noqa
123
159
  )
124
160
 
125
161
  from .providers import ( # noqa
@@ -153,16 +189,8 @@ with _lang.auto_proxy_init(globals()):
153
189
  create_injector,
154
190
  )
155
191
 
156
- from .tags import ( # noqa
157
- Id,
158
- )
159
-
160
192
  from .types import ( # noqa
161
193
  Scope,
162
194
  Tag,
163
195
  Unscoped,
164
196
  )
165
-
166
- from .utils import ( # noqa
167
- ConstFn,
168
- )