omserv 0.0.0.dev280__py3-none-any.whl → 0.0.0.dev282__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.
omserv/apps/base.py CHANGED
@@ -1,13 +1,13 @@
1
1
  import contextvars
2
2
  import typing as ta
3
3
 
4
- from omlish.http.asgi import AsgiScope
4
+ from omlish.http import asgi
5
5
 
6
6
 
7
7
  ##
8
8
 
9
9
 
10
- SCOPE: contextvars.ContextVar[AsgiScope] = contextvars.ContextVar('scope')
10
+ SCOPE: contextvars.ContextVar[asgi.Scope] = contextvars.ContextVar('scope')
11
11
 
12
12
 
13
13
  ##
omserv/apps/inject.py CHANGED
@@ -1,18 +1,17 @@
1
1
  import typing as ta
2
2
 
3
3
  from omlish import inject as inj
4
+ from omlish.http import asgi
4
5
  from omlish.http import sessions
5
- from omlish.http.asgi import AsgiApp
6
- from omlish.http.asgi import AsgiScope
7
6
 
8
7
  from .base import SCOPE
9
8
  from .markers import AppMarker
10
9
  from .markers import AppMarkerProcessor
11
10
  from .markers import NopAppMarkerProcessor
12
- from .markers import get_app_markers
13
- from .routes import Handler_
14
11
  from .routes import Route
12
+ from .routes import RouteHandlerHolder
15
13
  from .routes import _HandlesAppMarker
14
+ from .routes import build_route_handler_map
16
15
  from .sessions import SESSION
17
16
  from .sessions import _WithSessionAppMarker
18
17
  from .sessions import _WithSessionAppMarkerProcessor
@@ -20,10 +19,10 @@ from .templates import JinjaNamespace
20
19
  from .templates import JinjaTemplates
21
20
 
22
21
 
23
- def bind_handler(hc: type[Handler_]) -> inj.Elemental:
22
+ def bind_route_handler_class(hc: type[RouteHandlerHolder]) -> inj.Elemental:
24
23
  return inj.as_elements(
25
24
  inj.bind(hc, singleton=True),
26
- inj.set_binder[Handler_]().bind(hc),
25
+ inj.set_binder[RouteHandlerHolder]().bind(hc),
27
26
  )
28
27
 
29
28
 
@@ -35,20 +34,13 @@ def bind_app_marker_processor(mc: type[AppMarker], pc: type[AppMarkerProcessor])
35
34
 
36
35
 
37
36
  def _build_route_handler_map(
38
- handlers: ta.AbstractSet[Handler_],
37
+ handler_holders: ta.AbstractSet[RouteHandlerHolder],
39
38
  processors: ta.Mapping[type[AppMarker], AppMarkerProcessor],
40
- ) -> ta.Mapping[Route, AsgiApp]:
41
- route_handlers: dict[Route, AsgiApp] = {}
42
- for h in handlers:
43
- for rh in h.get_route_handlers():
44
- app = rh.handler
45
- markers = get_app_markers(rh.handler)
46
- for m in markers:
47
- mp = processors[type(m)]
48
- if mp is not None:
49
- app = mp(app)
50
- route_handlers[rh.route] = app
51
- return route_handlers
39
+ ) -> ta.Mapping[Route, asgi.App]:
40
+ return build_route_handler_map(
41
+ handler_holders,
42
+ processors,
43
+ )
52
44
 
53
45
 
54
46
  def bind_route_handler_map() -> inj.Elemental:
@@ -60,7 +52,7 @@ def bind_route_handler_map() -> inj.Elemental:
60
52
 
61
53
  def bind() -> inj.Elemental:
62
54
  return inj.as_elements(
63
- inj.bind(ta.Callable[[], AsgiScope], to_const=SCOPE.get),
55
+ inj.bind(ta.Callable[[], asgi.Scope], to_const=SCOPE.get),
64
56
  inj.bind(ta.Callable[[], sessions.Session], to_const=SESSION.get),
65
57
 
66
58
  ##
@@ -72,7 +64,7 @@ def bind() -> inj.Elemental:
72
64
 
73
65
  ##
74
66
 
75
- inj.set_binder[Handler_](),
67
+ inj.set_binder[RouteHandlerHolder](),
76
68
  )
77
69
 
78
70
 
omserv/apps/markers.py CHANGED
@@ -2,7 +2,7 @@ import abc
2
2
  import typing as ta
3
3
 
4
4
  from omlish import lang
5
- from omlish.http.asgi import AsgiApp
5
+ from omlish.http import asgi
6
6
  from omlish.metadata import ObjectMetadata
7
7
  from omlish.metadata import append_object_metadata
8
8
  from omlish.metadata import get_object_metadata
@@ -35,10 +35,16 @@ def get_app_markers(obj: ta.Any) -> ta.Sequence[AppMarker]:
35
35
 
36
36
  class AppMarkerProcessor(lang.Abstract):
37
37
  @abc.abstractmethod
38
- def __call__(self, app: AsgiApp) -> AsgiApp:
38
+ def process_app(self, app: asgi.App) -> asgi.App:
39
39
  raise NotImplementedError
40
40
 
41
41
 
42
42
  class NopAppMarkerProcessor(AppMarkerProcessor, lang.Final):
43
- def __call__(self, app: AsgiApp) -> AsgiApp:
43
+ def process_app(self, app: asgi.App) -> asgi.App:
44
44
  return app
45
+
46
+
47
+ ##
48
+
49
+
50
+ AppMarkerProcessorMap: ta.TypeAlias = ta.Mapping[type[AppMarker], AppMarkerProcessor]
omserv/apps/routes.py CHANGED
@@ -1,3 +1,8 @@
1
+ """
2
+ TODO:
3
+ - include route in process_app?
4
+ - or, deduplicate handlers and only process apps once?
5
+ """
1
6
  import contextlib
2
7
  import dataclasses as dc
3
8
  import logging
@@ -5,18 +10,15 @@ import typing as ta
5
10
 
6
11
  from omlish import check
7
12
  from omlish import lang
8
- from omlish.http.asgi import AsgiApp
9
- from omlish.http.asgi import AsgiApp_
10
- from omlish.http.asgi import AsgiRecv
11
- from omlish.http.asgi import AsgiScope
12
- from omlish.http.asgi import AsgiSend
13
- from omlish.http.asgi import send_response
14
- from omlish.http.asgi import stub_lifespan
13
+ from omlish.http import asgi
15
14
 
16
15
  from .base import BASE_SERVER_URL
17
16
  from .base import SCOPE
18
17
  from .base import BaseServerUrl
19
18
  from .markers import AppMarker
19
+ from .markers import AppMarkerProcessor
20
+ from .markers import AppMarkerProcessorMap
21
+ from .markers import NopAppMarkerProcessor
20
22
  from .markers import append_app_marker
21
23
  from .markers import get_app_markers
22
24
 
@@ -50,7 +52,7 @@ class Route(ta.NamedTuple):
50
52
 
51
53
  class RouteHandler(ta.NamedTuple):
52
54
  route: Route
53
- handler: AsgiApp
55
+ handler: asgi.App
54
56
 
55
57
 
56
58
  @dc.dataclass(frozen=True)
@@ -67,15 +69,20 @@ def handles(*routes: Route):
67
69
  return inner
68
70
 
69
71
 
72
+ HANDLES_APP_MARKER_PROCESSORS: AppMarkerProcessorMap = {
73
+ _HandlesAppMarker: NopAppMarkerProcessor(),
74
+ }
75
+
76
+
70
77
  ##
71
78
 
72
79
 
73
- class Handler_(lang.Abstract): # noqa
80
+ class RouteHandlerHolder(lang.Abstract): # noqa
74
81
  def get_route_handlers(self) -> ta.Iterable[RouteHandler]:
75
82
  return get_marked_route_handlers(self)
76
83
 
77
84
 
78
- def get_marked_route_handlers(h: Handler_) -> ta.Sequence[RouteHandler]:
85
+ def get_marked_route_handlers(h: RouteHandlerHolder) -> ta.Sequence[RouteHandler]:
79
86
  ret: list[RouteHandler] = []
80
87
 
81
88
  cdct: dict[str, ta.Any] = {}
@@ -96,21 +103,67 @@ def get_marked_route_handlers(h: Handler_) -> ta.Sequence[RouteHandler]:
96
103
  return ret
97
104
 
98
105
 
106
+ @dc.dataclass()
107
+ class DuplicateRouteError(Exception):
108
+ route_handlers: ta.Sequence[RouteHandler]
109
+
110
+
111
+ def build_route_handler_map(
112
+ handlers: ta.Iterable[RouteHandler | RouteHandlerHolder],
113
+ processors: ta.Mapping[type[AppMarker], AppMarkerProcessor],
114
+ ) -> ta.Mapping[Route, asgi.App]:
115
+ rh_by_r: dict[Route, RouteHandler] = {}
116
+ for h in handlers:
117
+ if isinstance(h, RouteHandlerHolder):
118
+ rhs = list(h.get_route_handlers())
119
+ else:
120
+ rhs = [h]
121
+
122
+ for rh in rhs:
123
+ try:
124
+ ex = rh_by_r[rh.route]
125
+ except KeyError:
126
+ pass
127
+ else:
128
+ raise DuplicateRouteError([rh, ex])
129
+
130
+ rh_by_r[rh.route] = rh
131
+
132
+ app_by_r: dict[Route, asgi.App] = {}
133
+ for r, rh in rh_by_r.items():
134
+ app = rh.handler
135
+
136
+ markers = get_app_markers(rh.handler)
137
+ for m in markers:
138
+ mp = processors[type(m)]
139
+ if mp is not None:
140
+ app = mp.process_app(app)
141
+
142
+ app_by_r[r] = app
143
+
144
+ return app_by_r
145
+
146
+
99
147
  ##
100
148
 
101
149
 
102
150
  @dc.dataclass(frozen=True)
103
- class RouteHandlerApp(AsgiApp_):
104
- route_handlers: ta.Mapping[Route, AsgiApp]
151
+ class RouteHandlerApp(asgi.App_):
152
+ route_handlers: ta.Mapping[Route, asgi.App]
105
153
  base_server_url: BaseServerUrl | None = None
106
154
 
107
- async def __call__(self, scope: AsgiScope, recv: AsgiRecv, send: AsgiSend) -> None:
155
+ URL_SCHEME_PORT_PAIRS: ta.ClassVar[ta.Collection[tuple[str, int]]] = (
156
+ ('http', 80),
157
+ ('https', 443),
158
+ )
159
+
160
+ async def __call__(self, scope: asgi.Scope, recv: asgi.Recv, send: asgi.Send) -> None:
108
161
  with contextlib.ExitStack() as es:
109
162
  es.enter_context(lang.context_var_setting(SCOPE, scope)) # noqa
110
163
 
111
164
  match scope_ty := scope['type']:
112
165
  case 'lifespan':
113
- await stub_lifespan(scope, recv, send)
166
+ await asgi.stub_lifespan(scope, recv, send)
114
167
  return
115
168
 
116
169
  case 'http':
@@ -119,7 +172,7 @@ class RouteHandlerApp(AsgiApp_):
119
172
  else:
120
173
  sch = scope['scheme']
121
174
  h, p = scope['server']
122
- if (sch, p) not in (('http', 80), ('https', 443)):
175
+ if (sch, p) not in self.URL_SCHEME_PORT_PAIRS:
123
176
  ps = f':{p}'
124
177
  else:
125
178
  ps = ''
@@ -133,7 +186,7 @@ class RouteHandlerApp(AsgiApp_):
133
186
  await handler(scope, recv, send)
134
187
 
135
188
  else:
136
- await send_response(send, 404)
189
+ await asgi.send_response(send, 404)
137
190
 
138
191
  case _:
139
192
  raise ValueError(f'Unhandled scope type: {scope_ty!r}')
omserv/apps/sessions.py CHANGED
@@ -3,11 +3,8 @@ import dataclasses as dc
3
3
  import logging
4
4
 
5
5
  from omlish import lang
6
+ from omlish.http import asgi
6
7
  from omlish.http import sessions
7
- from omlish.http.asgi import AsgiApp
8
- from omlish.http.asgi import AsgiRecv
9
- from omlish.http.asgi import AsgiScope
10
- from omlish.http.asgi import AsgiSend
11
8
 
12
9
  from .markers import AppMarker
13
10
  from .markers import AppMarkerProcessor
@@ -35,7 +32,7 @@ def with_session(fn):
35
32
  class _WithSessionAppMarkerProcessor(AppMarkerProcessor):
36
33
  _ss: sessions.CookieSessionStore
37
34
 
38
- async def _wrap(self, fn: AsgiApp, scope: AsgiScope, recv: AsgiRecv, send: AsgiSend) -> None:
35
+ async def _wrap(self, fn: asgi.App, scope: asgi.Scope, recv: asgi.Recv, send: asgi.Send) -> None:
39
36
  async def _send(obj):
40
37
  if obj['type'] == 'http.response.start':
41
38
  out_session = SESSION.get()
@@ -53,5 +50,5 @@ class _WithSessionAppMarkerProcessor(AppMarkerProcessor):
53
50
  with lang.context_var_setting(SESSION, in_session):
54
51
  await fn(scope, recv, _send)
55
52
 
56
- def __call__(self, app: AsgiApp) -> AsgiApp:
53
+ def process_app(self, app: asgi.App) -> asgi.App:
57
54
  return lang.decorator(self._wrap)(app) # noqa
omserv/server/types.py CHANGED
@@ -4,13 +4,13 @@ import typing as ta
4
4
  ##
5
5
 
6
6
 
7
- AsgiReceiveEvent: ta.TypeAlias = dict[str, ta.Any]
8
- AsgiSendEvent: ta.TypeAlias = dict[str, ta.Any]
7
+ AsgiReceiveEvent: ta.TypeAlias = ta.Mapping[str, ta.Any]
8
+ AsgiSendEvent: ta.TypeAlias = ta.Mapping[str, ta.Any]
9
9
 
10
10
  AsgiReceiveCallable: ta.TypeAlias = ta.Callable[[], ta.Awaitable[AsgiReceiveEvent]]
11
11
  AsgiSendCallable: ta.TypeAlias = ta.Callable[[AsgiSendEvent], ta.Awaitable[None]]
12
12
 
13
- Scope: ta.TypeAlias = dict[str, ta.Any]
13
+ Scope: ta.TypeAlias = ta.Mapping[str, ta.Any]
14
14
  AsgiFramework: ta.TypeAlias = ta.Callable[
15
15
  [
16
16
  Scope,
@@ -22,13 +22,13 @@ AsgiFramework: ta.TypeAlias = ta.Callable[
22
22
 
23
23
  LifespanScope: ta.TypeAlias = Scope
24
24
 
25
- HttpResponseStartEvent: ta.TypeAlias = dict[str, ta.Any]
25
+ HttpResponseStartEvent: ta.TypeAlias = ta.Mapping[str, ta.Any]
26
26
  HttpScope: ta.TypeAlias = Scope
27
27
  WebsocketScope: ta.TypeAlias = Scope
28
28
 
29
- WebsocketAcceptEvent: ta.TypeAlias = dict[str, ta.Any]
30
- WebsocketResponseBodyEvent: ta.TypeAlias = dict[str, ta.Any]
31
- WebsocketResponseStartEvent: ta.TypeAlias = dict[str, ta.Any]
29
+ WebsocketAcceptEvent: ta.TypeAlias = ta.Mapping[str, ta.Any]
30
+ WebsocketResponseBodyEvent: ta.TypeAlias = ta.Mapping[str, ta.Any]
31
+ WebsocketResponseStartEvent: ta.TypeAlias = ta.Mapping[str, ta.Any]
32
32
 
33
33
 
34
34
  ##
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omserv
3
- Version: 0.0.0.dev280
3
+ Version: 0.0.0.dev282
4
4
  Summary: omserv
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish==0.0.0.dev280
15
+ Requires-Dist: omlish==0.0.0.dev282
16
16
  Provides-Extra: all
17
17
  Requires-Dist: h11~=0.14; extra == "all"
18
18
  Requires-Dist: h2~=4.2; extra == "all"
@@ -2,11 +2,11 @@ omserv/.manifests.json,sha256=qn9Vc6VXiVo9RzpJdLzQi_x2DPZIP3cq4S2bkqvJqjY,260
2
2
  omserv/__about__.py,sha256=w4jEBHhEVT7VZlwqMAh-4SecngU8xM6rehzWqi5ULZM,765
3
3
  omserv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omserv/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- omserv/apps/base.py,sha256=KSwxbC0_fY87_DucvpEOHG6gZ2wL78D-N3We8RTEU4Q,385
6
- omserv/apps/inject.py,sha256=xtcn-C3A4yCdHfnAaejxrC_UGMVBYmWtSxeESIkorP0,2697
7
- omserv/apps/markers.py,sha256=JyVEXIaZC01bYObZ08TCwmQgiqZSvPfwNFJwpt9PU-c,870
8
- omserv/apps/routes.py,sha256=xWVgMwMeRr1QJiCCB_LCtxsb41ajdTmbDua4X71Djo0,3778
9
- omserv/apps/sessions.py,sha256=glruQSbOSbCYLPp6nDRNSHCyp5hj4oiOPhh3R0F9BTM,1537
5
+ omserv/apps/base.py,sha256=HYsWbDot7zOnsdSL8dF_ANrNXEYgWIGzlnBdCU7n8v8,376
6
+ omserv/apps/inject.py,sha256=GxM-TT3Ln8YrYBedJF__wY-zs4G3ow7RlxT2afr0FDs,2416
7
+ omserv/apps/markers.py,sha256=JE_px0vsJqoDIDRFu9xtXsKjtxYAshUsFB0aRsoeIcg,965
8
+ omserv/apps/routes.py,sha256=GeP1fk-tdd8LQ2no_FO2bFi48gSqjmTXpLTtcpsvSa0,5096
9
+ omserv/apps/sessions.py,sha256=4HFHra43S0TaM2OcWUZ06eQwAqEaXuhjK5X5jlI0_44,1423
10
10
  omserv/apps/templates.py,sha256=PBRZHIF9UbnFnq-4EC6RmPeRkeH8lCBbpJkSdseHs6A,2125
11
11
  omserv/nginx/__init__.py,sha256=2d63LCGFA2qS7gdl2nCvNPmQWXNICu19wZIihQJPHCs,48
12
12
  omserv/nginx/build.py,sha256=87o2IahwUC-xriDpqc3OLKwwx4DacrDtUTplNQe_36k,3203
@@ -33,7 +33,7 @@ omserv/server/server.py,sha256=hgUTfZAUwF7V-HdsdV98KbYmE1IRKmEO3B1LlKTiPWc,5223
33
33
  omserv/server/sockets.py,sha256=lwqNP7URlp605ibsjHzp0pc-lyjcyTu-hD-uyojLUYk,3389
34
34
  omserv/server/ssl.py,sha256=gmB5ecM8Mck-YtGYF8pb2dwFdjABVGzERFCDzM9lBck,1483
35
35
  omserv/server/taskspawner.py,sha256=ljzF26UPtnp7GLAY_BvjzuwCoCO9aL7TKLwRNTmUy1M,3008
36
- omserv/server/types.py,sha256=XXY5py8RYlEeD4FZrWNqSyX7DD-ffSlcG-T2s9BY9eI,2017
36
+ omserv/server/types.py,sha256=02GcS_zDxBzkleaptwr7kT7viefNh7fhFJixPdDpL_M,2059
37
37
  omserv/server/workercontext.py,sha256=4rcLuGsyiU7URO7T_eHylOBPPNUS9C23QfEUVyJUtIY,1200
38
38
  omserv/server/protocols/__init__.py,sha256=Ryu2PDZ1TUI6F2l-HBEYgyzZ7wHqE6VmjqnS0tIvmQI,47
39
39
  omserv/server/protocols/h11.py,sha256=_q_paD-ff0AWJEPaNK-6MUsQVtYRiALnWGwFyM3D0KU,11976
@@ -46,9 +46,9 @@ omserv/server/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
46
46
  omserv/server/streams/httpstream.py,sha256=0DeiAPLGbEGNa0fHTs8lUpi_CFZs4M5_QB-TiS8mobQ,8015
47
47
  omserv/server/streams/utils.py,sha256=aMOrqWIg_Hht5W4kLg3y7oR5AEkVvMrZhyjzo6U5owE,1527
48
48
  omserv/server/streams/wsstream.py,sha256=3Vyzox7dCE1tDSXjb6xBubWo41ZF9d38Hrsrlj6h1J8,15482
49
- omserv-0.0.0.dev280.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
50
- omserv-0.0.0.dev280.dist-info/METADATA,sha256=P1USQSjnjruJHdPBkuxjwBFgf8SDOwa95DhY9Qg4uik,1005
51
- omserv-0.0.0.dev280.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
52
- omserv-0.0.0.dev280.dist-info/entry_points.txt,sha256=ivSrdA_ahEbI-eVMu-XZS-z4VrnQISvpecIkOqC9zFM,35
53
- omserv-0.0.0.dev280.dist-info/top_level.txt,sha256=HXehpnxeKscKNULzKNzZ27oNawBrsh1PaNAirbX-XNA,7
54
- omserv-0.0.0.dev280.dist-info/RECORD,,
49
+ omserv-0.0.0.dev282.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
50
+ omserv-0.0.0.dev282.dist-info/METADATA,sha256=AohL-vW9TC4ir0IKC54vRSEJtJladp8TN-mAyMZVD5g,1005
51
+ omserv-0.0.0.dev282.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
52
+ omserv-0.0.0.dev282.dist-info/entry_points.txt,sha256=ivSrdA_ahEbI-eVMu-XZS-z4VrnQISvpecIkOqC9zFM,35
53
+ omserv-0.0.0.dev282.dist-info/top_level.txt,sha256=HXehpnxeKscKNULzKNzZ27oNawBrsh1PaNAirbX-XNA,7
54
+ omserv-0.0.0.dev282.dist-info/RECORD,,