omserv 0.0.0.dev281__py3-none-any.whl → 0.0.0.dev283__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,14 +1,15 @@
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 AsgiScope
6
6
 
7
7
  from .base import SCOPE
8
8
  from .markers import AppMarker
9
9
  from .markers import AppMarkerProcessor
10
10
  from .markers import NopAppMarkerProcessor
11
- from .routes import RouteHandler_
11
+ from .routes import Route
12
+ from .routes import RouteHandlerHolder
12
13
  from .routes import _HandlesAppMarker
13
14
  from .routes import build_route_handler_map
14
15
  from .sessions import SESSION
@@ -18,10 +19,10 @@ from .templates import JinjaNamespace
18
19
  from .templates import JinjaTemplates
19
20
 
20
21
 
21
- def bind_handler(hc: type[RouteHandler_]) -> inj.Elemental:
22
+ def bind_route_handler_class(hc: type[RouteHandlerHolder]) -> inj.Elemental:
22
23
  return inj.as_elements(
23
24
  inj.bind(hc, singleton=True),
24
- inj.set_binder[RouteHandler_]().bind(hc),
25
+ inj.set_binder[RouteHandlerHolder]().bind(hc),
25
26
  )
26
27
 
27
28
 
@@ -32,16 +33,26 @@ def bind_app_marker_processor(mc: type[AppMarker], pc: type[AppMarkerProcessor])
32
33
  )
33
34
 
34
35
 
36
+ def _build_route_handler_map(
37
+ handler_holders: ta.AbstractSet[RouteHandlerHolder],
38
+ processors: ta.Mapping[type[AppMarker], AppMarkerProcessor],
39
+ ) -> ta.Mapping[Route, asgi.App]:
40
+ return build_route_handler_map(
41
+ handler_holders,
42
+ processors,
43
+ )
44
+
45
+
35
46
  def bind_route_handler_map() -> inj.Elemental:
36
47
  return inj.as_elements(
37
- inj.bind(build_route_handler_map, singleton=True),
48
+ inj.bind(_build_route_handler_map, singleton=True),
38
49
  inj.map_binder[type[AppMarker], AppMarkerProcessor](),
39
50
  )
40
51
 
41
52
 
42
53
  def bind() -> inj.Elemental:
43
54
  return inj.as_elements(
44
- inj.bind(ta.Callable[[], AsgiScope], to_const=SCOPE.get),
55
+ inj.bind(ta.Callable[[], asgi.Scope], to_const=SCOPE.get),
45
56
  inj.bind(ta.Callable[[], sessions.Session], to_const=SESSION.get),
46
57
 
47
58
  ##
@@ -53,7 +64,7 @@ def bind() -> inj.Elemental:
53
64
 
54
65
  ##
55
66
 
56
- inj.set_binder[RouteHandler_](),
67
+ inj.set_binder[RouteHandlerHolder](),
57
68
  )
58
69
 
59
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,12 +35,12 @@ 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
45
 
46
46
 
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,13 +10,7 @@ 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
@@ -53,7 +52,7 @@ class Route(ta.NamedTuple):
53
52
 
54
53
  class RouteHandler(ta.NamedTuple):
55
54
  route: Route
56
- handler: AsgiApp
55
+ handler: asgi.App
57
56
 
58
57
 
59
58
  @dc.dataclass(frozen=True)
@@ -78,12 +77,12 @@ HANDLES_APP_MARKER_PROCESSORS: AppMarkerProcessorMap = {
78
77
  ##
79
78
 
80
79
 
81
- class RouteHandler_(lang.Abstract): # noqa
80
+ class RouteHandlerHolder(lang.Abstract): # noqa
82
81
  def get_route_handlers(self) -> ta.Iterable[RouteHandler]:
83
82
  return get_marked_route_handlers(self)
84
83
 
85
84
 
86
- def get_marked_route_handlers(h: RouteHandler_) -> ta.Sequence[RouteHandler]:
85
+ def get_marked_route_handlers(h: RouteHandlerHolder) -> ta.Sequence[RouteHandler]:
87
86
  ret: list[RouteHandler] = []
88
87
 
89
88
  cdct: dict[str, ta.Any] = {}
@@ -104,38 +103,67 @@ def get_marked_route_handlers(h: RouteHandler_) -> ta.Sequence[RouteHandler]:
104
103
  return ret
105
104
 
106
105
 
106
+ @dc.dataclass()
107
+ class DuplicateRouteError(Exception):
108
+ route_handlers: ta.Sequence[RouteHandler]
109
+
110
+
107
111
  def build_route_handler_map(
108
- handlers: ta.AbstractSet[RouteHandler_],
112
+ handlers: ta.Iterable[RouteHandler | RouteHandlerHolder],
109
113
  processors: ta.Mapping[type[AppMarker], AppMarkerProcessor],
110
- ) -> ta.Mapping[Route, AsgiApp]:
111
- route_handlers: dict[Route, AsgiApp] = {}
114
+ ) -> ta.Mapping[Route, asgi.App]:
115
+ rh_by_r: dict[Route, RouteHandler] = {}
112
116
  for h in handlers:
113
- for rh in h.get_route_handlers():
114
- app = rh.handler
115
- markers = get_app_markers(rh.handler)
116
- for m in markers:
117
- mp = processors[type(m)]
118
- if mp is not None:
119
- app = mp(app)
120
- route_handlers[rh.route] = app
121
- return route_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
122
145
 
123
146
 
124
147
  ##
125
148
 
126
149
 
127
150
  @dc.dataclass(frozen=True)
128
- class RouteHandlerApp(AsgiApp_):
129
- route_handlers: ta.Mapping[Route, AsgiApp]
151
+ class RouteHandlerApp(asgi.App_):
152
+ route_handlers: ta.Mapping[Route, asgi.App]
130
153
  base_server_url: BaseServerUrl | None = None
131
154
 
132
- 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:
133
161
  with contextlib.ExitStack() as es:
134
162
  es.enter_context(lang.context_var_setting(SCOPE, scope)) # noqa
135
163
 
136
164
  match scope_ty := scope['type']:
137
165
  case 'lifespan':
138
- await stub_lifespan(scope, recv, send)
166
+ await asgi.stub_lifespan(scope, recv, send)
139
167
  return
140
168
 
141
169
  case 'http':
@@ -144,7 +172,7 @@ class RouteHandlerApp(AsgiApp_):
144
172
  else:
145
173
  sch = scope['scheme']
146
174
  h, p = scope['server']
147
- if (sch, p) not in (('http', 80), ('https', 443)):
175
+ if (sch, p) not in self.URL_SCHEME_PORT_PAIRS:
148
176
  ps = f':{p}'
149
177
  else:
150
178
  ps = ''
@@ -158,7 +186,7 @@ class RouteHandlerApp(AsgiApp_):
158
186
  await handler(scope, recv, send)
159
187
 
160
188
  else:
161
- await send_response(send, 404)
189
+ await asgi.send_response(send, 404)
162
190
 
163
191
  case _:
164
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omserv
3
- Version: 0.0.0.dev281
3
+ Version: 0.0.0.dev283
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.dev281
15
+ Requires-Dist: omlish==0.0.0.dev283
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=ora2yPGl8vz1vdLNKQYa_NuUHyMUo9JRHkMNbvN6joA,2083
7
- omserv/apps/markers.py,sha256=sLTd6d2HYH_lc37nDtDQK_ej10G9qeg4m_TC_v0IW6k,963
8
- omserv/apps/routes.py,sha256=n7LBAzkclUlWfhk2e2cv0GR5EkMDUpjqhyXWMWwTeVY,4604
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
@@ -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.dev281.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
50
- omserv-0.0.0.dev281.dist-info/METADATA,sha256=6kaceg5VdEC213N0KXR4OlC9d5GxYFXD56bg3bB74yk,1005
51
- omserv-0.0.0.dev281.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
52
- omserv-0.0.0.dev281.dist-info/entry_points.txt,sha256=ivSrdA_ahEbI-eVMu-XZS-z4VrnQISvpecIkOqC9zFM,35
53
- omserv-0.0.0.dev281.dist-info/top_level.txt,sha256=HXehpnxeKscKNULzKNzZ27oNawBrsh1PaNAirbX-XNA,7
54
- omserv-0.0.0.dev281.dist-info/RECORD,,
49
+ omserv-0.0.0.dev283.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
50
+ omserv-0.0.0.dev283.dist-info/METADATA,sha256=DXvfjW99UQEHijbV6UzEkFzjW4JOYEsCgx7hmpYZnxI,1005
51
+ omserv-0.0.0.dev283.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
52
+ omserv-0.0.0.dev283.dist-info/entry_points.txt,sha256=ivSrdA_ahEbI-eVMu-XZS-z4VrnQISvpecIkOqC9zFM,35
53
+ omserv-0.0.0.dev283.dist-info/top_level.txt,sha256=HXehpnxeKscKNULzKNzZ27oNawBrsh1PaNAirbX-XNA,7
54
+ omserv-0.0.0.dev283.dist-info/RECORD,,