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.
- omlish/.omlish-manifests.json +12 -0
- omlish/README.md +199 -0
- omlish/__about__.py +21 -16
- omlish/argparse/all.py +17 -9
- omlish/argparse/cli.py +16 -3
- omlish/argparse/utils.py +21 -0
- omlish/asyncs/asyncio/rlock.py +110 -0
- omlish/asyncs/asyncio/sync.py +43 -0
- omlish/asyncs/asyncio/utils.py +2 -0
- omlish/asyncs/sync.py +25 -0
- omlish/bootstrap/_marshal.py +1 -1
- omlish/bootstrap/diag.py +12 -21
- omlish/bootstrap/main.py +2 -5
- omlish/bootstrap/sys.py +27 -28
- omlish/cexts/__init__.py +0 -0
- omlish/cexts/include/omlish/omlish.hh +1 -0
- omlish/collections/__init__.py +13 -1
- omlish/collections/attrregistry.py +210 -0
- omlish/collections/cache/impl.py +1 -0
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +28 -0
- omlish/collections/trie.py +5 -1
- omlish/collections/utils.py +77 -0
- omlish/concurrent/all.py +2 -1
- omlish/concurrent/futures.py +25 -0
- omlish/concurrent/threadlets.py +1 -1
- omlish/daemons/reparent.py +2 -3
- omlish/daemons/spawning.py +2 -3
- omlish/dataclasses/__init__.py +2 -0
- omlish/dataclasses/impl/api/classes/decorator.py +3 -0
- omlish/dataclasses/impl/api/classes/make.py +3 -0
- omlish/dataclasses/impl/concerns/repr.py +15 -2
- omlish/dataclasses/impl/configs.py +97 -37
- omlish/dataclasses/impl/generation/compilation.py +21 -19
- omlish/dataclasses/impl/generation/globals.py +1 -0
- omlish/dataclasses/impl/generation/ops.py +1 -0
- omlish/dataclasses/impl/generation/plans.py +2 -17
- omlish/dataclasses/impl/generation/processor.py +106 -25
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +19 -7
- omlish/dataclasses/specs.py +1 -0
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/diag/_pycharm/runhack.py +1 -1
- omlish/diag/cmds/__init__.py +0 -0
- omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
- omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
- omlish/diag/{ps.py → cmds/ps.py} +6 -6
- omlish/diag/pycharm.py +16 -2
- omlish/diag/pydevd.py +58 -40
- omlish/diag/replserver/console.py +1 -1
- omlish/dispatch/__init__.py +18 -12
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/formats/dotenv.py +1 -1
- omlish/formats/json/stream/__init__.py +13 -0
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/http/all.py +44 -4
- omlish/http/clients/asyncs.py +33 -27
- omlish/http/clients/base.py +17 -1
- omlish/http/clients/coro/__init__.py +0 -0
- omlish/http/clients/coro/sync.py +171 -0
- omlish/http/clients/default.py +208 -29
- omlish/http/clients/executor.py +56 -0
- omlish/http/clients/httpx.py +72 -11
- omlish/http/clients/middleware.py +181 -0
- omlish/http/clients/sync.py +33 -27
- omlish/http/clients/syncasync.py +49 -0
- omlish/http/clients/urllib.py +6 -3
- omlish/http/coro/client/connection.py +15 -6
- omlish/http/coro/io.py +2 -0
- omlish/http/flasky/__init__.py +40 -0
- omlish/http/flasky/_compat.py +2 -0
- omlish/http/flasky/api.py +82 -0
- omlish/http/flasky/app.py +203 -0
- omlish/http/flasky/cvs.py +59 -0
- omlish/http/flasky/requests.py +20 -0
- omlish/http/flasky/responses.py +23 -0
- omlish/http/flasky/routes.py +23 -0
- omlish/http/flasky/types.py +15 -0
- omlish/http/urls.py +67 -0
- omlish/inject/__init__.py +57 -29
- omlish/inject/_dataclasses.py +5148 -0
- omlish/inject/binder.py +11 -52
- omlish/inject/eagers.py +2 -0
- omlish/inject/elements.py +27 -0
- omlish/inject/helpers/__init__.py +0 -0
- omlish/inject/{utils.py → helpers/constfn.py} +3 -3
- omlish/inject/{tags.py → helpers/id.py} +2 -2
- omlish/inject/helpers/late.py +76 -0
- omlish/inject/{managed.py → helpers/managed.py} +10 -10
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +54 -21
- omlish/inject/impl/injector.py +29 -25
- omlish/inject/impl/inspect.py +10 -1
- omlish/inject/impl/maysync.py +3 -4
- omlish/inject/impl/multis.py +3 -0
- omlish/inject/impl/sync.py +3 -4
- omlish/inject/injector.py +31 -2
- omlish/inject/inspect.py +35 -0
- omlish/inject/maysync.py +2 -4
- omlish/inject/multis.py +8 -0
- omlish/inject/overrides.py +3 -3
- omlish/inject/privates.py +6 -0
- omlish/inject/providers.py +3 -2
- omlish/inject/sync.py +5 -4
- omlish/io/buffers.py +118 -0
- omlish/io/readers.py +29 -0
- omlish/iterators/transforms.py +2 -2
- omlish/lang/__init__.py +180 -97
- omlish/lang/_asyncs.cc +186 -0
- omlish/lang/asyncs.py +17 -0
- omlish/lang/casing.py +11 -0
- omlish/lang/contextmanagers.py +28 -4
- omlish/lang/functions.py +31 -22
- omlish/lang/imports/_capture.cc +11 -9
- omlish/lang/imports/capture.py +363 -170
- omlish/lang/imports/proxy.py +455 -152
- omlish/lang/lazyglobals.py +22 -9
- omlish/lang/params.py +17 -0
- omlish/lang/recursion.py +0 -1
- omlish/lang/sequences.py +124 -0
- omlish/lifecycles/README.md +30 -0
- omlish/lifecycles/__init__.py +87 -13
- omlish/lifecycles/_dataclasses.py +1388 -0
- omlish/lifecycles/base.py +178 -64
- omlish/lifecycles/contextmanagers.py +113 -4
- omlish/lifecycles/controller.py +150 -87
- omlish/lifecycles/injection.py +143 -0
- omlish/lifecycles/listeners.py +56 -0
- omlish/lifecycles/managed.py +142 -0
- omlish/lifecycles/manager.py +218 -93
- omlish/lifecycles/states.py +2 -0
- omlish/lifecycles/transitions.py +3 -0
- omlish/lifecycles/unwrap.py +57 -0
- omlish/lite/abstract.py +54 -24
- omlish/lite/asyncs.py +2 -2
- omlish/lite/attrops.py +2 -0
- omlish/lite/contextmanagers.py +4 -4
- omlish/lite/dataclasses.py +44 -0
- omlish/lite/maybes.py +8 -0
- omlish/lite/maysync.py +1 -5
- omlish/lite/pycharm.py +1 -1
- omlish/lite/typing.py +24 -0
- omlish/logs/_amalg.py +1 -1
- omlish/logs/all.py +25 -11
- omlish/logs/asyncs.py +73 -0
- omlish/logs/base.py +101 -12
- omlish/logs/contexts.py +4 -1
- omlish/logs/lists.py +125 -0
- omlish/logs/modules.py +19 -1
- omlish/logs/std/loggers.py +6 -1
- omlish/logs/std/noisy.py +11 -9
- omlish/logs/{standard.py → std/standard.py} +3 -4
- omlish/logs/utils.py +17 -2
- omlish/manifests/loading.py +2 -1
- omlish/marshal/__init__.py +33 -13
- omlish/marshal/_dataclasses.py +2774 -0
- omlish/marshal/base/configs.py +12 -0
- omlish/marshal/base/contexts.py +36 -21
- omlish/marshal/base/funcs.py +8 -11
- omlish/marshal/base/options.py +8 -0
- omlish/marshal/base/registries.py +146 -44
- omlish/marshal/base/types.py +40 -16
- omlish/marshal/composite/iterables.py +33 -20
- omlish/marshal/composite/literals.py +20 -18
- omlish/marshal/composite/mappings.py +36 -23
- omlish/marshal/composite/maybes.py +29 -19
- omlish/marshal/composite/newtypes.py +16 -16
- omlish/marshal/composite/optionals.py +14 -14
- omlish/marshal/composite/special.py +15 -15
- omlish/marshal/composite/unions/__init__.py +0 -0
- omlish/marshal/composite/unions/literals.py +93 -0
- omlish/marshal/composite/unions/primitives.py +103 -0
- omlish/marshal/factories/invalidate.py +18 -68
- omlish/marshal/factories/method.py +26 -0
- omlish/marshal/factories/moduleimport/factories.py +22 -65
- omlish/marshal/factories/multi.py +13 -25
- omlish/marshal/factories/recursive.py +42 -56
- omlish/marshal/factories/typecache.py +29 -74
- omlish/marshal/factories/typemap.py +42 -43
- omlish/marshal/objects/dataclasses.py +129 -106
- omlish/marshal/objects/marshal.py +18 -14
- omlish/marshal/objects/namedtuples.py +48 -42
- omlish/marshal/objects/unmarshal.py +19 -15
- omlish/marshal/polymorphism/marshal.py +9 -11
- omlish/marshal/polymorphism/metadata.py +16 -5
- omlish/marshal/polymorphism/standard.py +13 -1
- omlish/marshal/polymorphism/unions.py +15 -105
- omlish/marshal/polymorphism/unmarshal.py +9 -10
- omlish/marshal/singular/enums.py +14 -18
- omlish/marshal/standard.py +10 -6
- omlish/marshal/trivial/any.py +1 -1
- omlish/marshal/trivial/forbidden.py +21 -26
- omlish/metadata.py +23 -1
- omlish/os/forkhooks.py +4 -0
- omlish/os/pidfiles/pinning.py +2 -2
- omlish/reflect/__init__.py +43 -26
- omlish/reflect/ops.py +10 -1
- omlish/reflect/types.py +1 -0
- omlish/secrets/marshal.py +1 -1
- omlish/specs/jmespath/__init__.py +12 -3
- omlish/specs/jmespath/_dataclasses.py +2893 -0
- omlish/specs/jmespath/ast.py +1 -1
- omlish/specs/jsonrpc/__init__.py +13 -0
- omlish/specs/jsonrpc/_marshal.py +32 -23
- omlish/specs/jsonrpc/conns.py +10 -7
- omlish/specs/jsonschema/_marshal.py +1 -1
- omlish/specs/jsonschema/keywords/__init__.py +7 -0
- omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
- omlish/specs/openapi/_marshal.py +31 -22
- omlish/sql/__init__.py +24 -5
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/api/dbapi.py +1 -1
- omlish/sql/dbapi/__init__.py +15 -0
- omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
- omlish/sql/queries/__init__.py +3 -0
- omlish/sql/queries/_marshal.py +2 -2
- omlish/sql/queries/rendering.py +1 -1
- omlish/sql/tabledefs/_marshal.py +1 -1
- omlish/subprocesses/base.py +4 -0
- omlish/subprocesses/editor.py +1 -1
- omlish/sync.py +155 -21
- omlish/term/alt.py +60 -0
- omlish/term/confirm.py +8 -8
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/term/vt100/terminal.py +0 -3
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
- omlish/testing/pytest/plugins/skips.py +2 -1
- omlish/testing/unittest/main.py +3 -3
- omlish/text/docwrap/__init__.py +3 -0
- omlish/text/docwrap/__main__.py +11 -0
- omlish/text/docwrap/api.py +83 -0
- omlish/text/docwrap/cli.py +91 -0
- omlish/text/docwrap/groups.py +84 -0
- omlish/text/docwrap/lists.py +167 -0
- omlish/text/docwrap/parts.py +146 -0
- omlish/text/docwrap/reflowing.py +106 -0
- omlish/text/docwrap/rendering.py +151 -0
- omlish/text/docwrap/utils.py +11 -0
- omlish/text/docwrap/wrapping.py +59 -0
- omlish/text/filecache.py +2 -2
- omlish/text/lorem.py +6 -0
- omlish/text/parts.py +2 -2
- omlish/text/textwrap.py +51 -0
- omlish/typedvalues/marshal.py +85 -59
- omlish/typedvalues/values.py +2 -1
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +36 -32
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/funcs/match.py +0 -227
- omlish/lifecycles/abstract.py +0 -86
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- /omlish/inject/{impl → helpers}/proxy.py +0 -0
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
- {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)
|
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
|
-
|
|
95
|
-
MapProvider,
|
|
126
|
+
is_set_multi_key,
|
|
96
127
|
SetBinding,
|
|
97
128
|
SetProvider,
|
|
98
|
-
|
|
129
|
+
|
|
130
|
+
is_map_multi_key,
|
|
131
|
+
MapBinding,
|
|
132
|
+
MapProvider,
|
|
133
|
+
|
|
99
134
|
SetBinder,
|
|
135
|
+
set_binder,
|
|
100
136
|
|
|
101
|
-
MapBinder
|
|
102
|
-
|
|
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
|
-
)
|