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 +2 -2
- omserv/apps/inject.py +13 -21
- omserv/apps/markers.py +9 -3
- omserv/apps/routes.py +69 -16
- omserv/apps/sessions.py +3 -6
- omserv/server/types.py +7 -7
- {omserv-0.0.0.dev280.dist-info → omserv-0.0.0.dev282.dist-info}/METADATA +2 -2
- {omserv-0.0.0.dev280.dist-info → omserv-0.0.0.dev282.dist-info}/RECORD +12 -12
- {omserv-0.0.0.dev280.dist-info → omserv-0.0.0.dev282.dist-info}/WHEEL +0 -0
- {omserv-0.0.0.dev280.dist-info → omserv-0.0.0.dev282.dist-info}/entry_points.txt +0 -0
- {omserv-0.0.0.dev280.dist-info → omserv-0.0.0.dev282.dist-info}/licenses/LICENSE +0 -0
- {omserv-0.0.0.dev280.dist-info → omserv-0.0.0.dev282.dist-info}/top_level.txt +0 -0
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
|
4
|
+
from omlish.http import asgi
|
5
5
|
|
6
6
|
|
7
7
|
##
|
8
8
|
|
9
9
|
|
10
|
-
SCOPE: contextvars.ContextVar[
|
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
|
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[
|
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
|
-
|
37
|
+
handler_holders: ta.AbstractSet[RouteHandlerHolder],
|
39
38
|
processors: ta.Mapping[type[AppMarker], AppMarkerProcessor],
|
40
|
-
) -> ta.Mapping[Route,
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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[[],
|
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[
|
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
|
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
|
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
|
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
|
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:
|
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
|
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:
|
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(
|
104
|
-
route_handlers: ta.Mapping[Route,
|
151
|
+
class RouteHandlerApp(asgi.App_):
|
152
|
+
route_handlers: ta.Mapping[Route, asgi.App]
|
105
153
|
base_server_url: BaseServerUrl | None = None
|
106
154
|
|
107
|
-
|
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
|
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:
|
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
|
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 =
|
8
|
-
AsgiSendEvent: ta.TypeAlias =
|
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 =
|
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 =
|
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 =
|
30
|
-
WebsocketResponseBodyEvent: ta.TypeAlias =
|
31
|
-
WebsocketResponseStartEvent: ta.TypeAlias =
|
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.
|
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.
|
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=
|
6
|
-
omserv/apps/inject.py,sha256=
|
7
|
-
omserv/apps/markers.py,sha256=
|
8
|
-
omserv/apps/routes.py,sha256=
|
9
|
-
omserv/apps/sessions.py,sha256=
|
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=
|
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.
|
50
|
-
omserv-0.0.0.
|
51
|
-
omserv-0.0.0.
|
52
|
-
omserv-0.0.0.
|
53
|
-
omserv-0.0.0.
|
54
|
-
omserv-0.0.0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|