omserv 0.0.0.dev160__py3-none-any.whl → 0.0.0.dev217__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/nginx/__init__.py CHANGED
@@ -0,0 +1,4 @@
1
+ """
2
+ TODO:
3
+ - https://github.com/yandex/gixy
4
+ """
omserv/nginx/build.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ TODO:
3
+ - https://github.com/webserver-llc/angie ?
4
+ """
1
5
  import multiprocessing as mp
2
6
  import os.path
3
7
  import shutil
omserv/server/__init__.py CHANGED
@@ -15,4 +15,4 @@ See:
15
15
  - https://github.com/tiangolo/fastapi
16
16
  """
17
17
  from .config import Config # noqa
18
- from .workers import serve # noqa
18
+ from .default import serve # noqa
@@ -0,0 +1,34 @@
1
+ import typing as ta
2
+
3
+ import anyio.abc
4
+
5
+ from omlish import inject as inj
6
+
7
+ from .config import Config
8
+ from .inject import bind_server
9
+ from .listener import Listener
10
+ from .sockets import Sockets
11
+ from .types import AppWrapper
12
+ from .types import AsgiFramework
13
+ from .types import wrap_app
14
+
15
+
16
+ async def serve(
17
+ app: AsgiFramework | AppWrapper,
18
+ config: Config,
19
+ *,
20
+ sockets: Sockets | None = None,
21
+ shutdown_trigger: ta.Callable[..., ta.Awaitable[None]] | None = None,
22
+ handle_shutdown_signals: bool = False,
23
+ task_status: anyio.abc.TaskStatus[ta.Sequence[str]] = anyio.TASK_STATUS_IGNORED,
24
+ ) -> None:
25
+ injector = inj.create_injector(bind_server(config))
26
+ listener = injector.inject(Listener)
27
+ await listener.listen(
28
+ wrap_app(app),
29
+ config,
30
+ sockets=sockets,
31
+ shutdown_trigger=shutdown_trigger,
32
+ handle_shutdown_signals=handle_shutdown_signals,
33
+ task_status=task_status,
34
+ )
@@ -0,0 +1,21 @@
1
+ import functools
2
+
3
+ from omlish import inject as inj
4
+
5
+ from .config import Config
6
+ from .server import Server
7
+ from .server import ServerFactory
8
+
9
+
10
+ def _provide_server_factory(config: Config) -> ServerFactory:
11
+ return ServerFactory(functools.partial(Server, config=config))
12
+
13
+
14
+ def bind_server(
15
+ config: Config,
16
+ ) -> inj.Elemental:
17
+ return inj.as_elements(
18
+ inj.bind(config),
19
+
20
+ inj.bind(_provide_server_factory, singleton=True),
21
+ )
@@ -11,10 +11,10 @@ import anyio.abc
11
11
 
12
12
  from .config import Config
13
13
  from .lifespans import Lifespan
14
+ from .server import ServerFactory
14
15
  from .sockets import Sockets
15
16
  from .sockets import create_sockets
16
17
  from .sockets import repr_socket_addr
17
- from .tcpserver import TcpServer
18
18
  from .types import AppWrapper
19
19
  from .types import AsgiFramework
20
20
  from .types import wrap_app
@@ -125,69 +125,80 @@ async def _install_signal_handler(
125
125
  return signal_event.wait
126
126
 
127
127
 
128
- async def serve(
129
- app: AsgiFramework | AppWrapper,
130
- config: Config,
131
- *,
132
- sockets: Sockets | None = None,
133
- shutdown_trigger: ta.Callable[..., ta.Awaitable[None]] | None = None,
134
- handle_shutdown_signals: bool = False,
135
- task_status: anyio.abc.TaskStatus[ta.Sequence[str]] = anyio.TASK_STATUS_IGNORED,
136
- ) -> None:
137
- app = wrap_app(app)
138
-
139
- lifespan = Lifespan(app, config)
140
- max_requests = None
141
- if config.max_requests is not None:
142
- max_requests = config.max_requests + random.randint(0, config.max_requests_jitter)
143
- context = WorkerContext(max_requests)
128
+ class Listener:
129
+ def __init__(
130
+ self,
131
+ *,
132
+ server_factory: ServerFactory,
133
+ ) -> None:
134
+ super().__init__()
135
+
136
+ self._server_factory = server_factory
137
+
138
+ async def listen(
139
+ self,
140
+ app: AsgiFramework | AppWrapper,
141
+ config: Config,
142
+ *,
143
+ sockets: Sockets | None = None,
144
+ shutdown_trigger: ta.Callable[..., ta.Awaitable[None]] | None = None,
145
+ handle_shutdown_signals: bool = False,
146
+ task_status: anyio.abc.TaskStatus[ta.Sequence[str]] = anyio.TASK_STATUS_IGNORED,
147
+ ) -> None:
148
+ app = wrap_app(app)
149
+
150
+ lifespan = Lifespan(app, config)
151
+ max_requests = None
152
+ if config.max_requests is not None:
153
+ max_requests = config.max_requests + random.randint(0, config.max_requests_jitter)
154
+ context = WorkerContext(max_requests)
155
+
156
+ async with anyio.create_task_group() as lifespan_task_group:
157
+ if shutdown_trigger is None and handle_shutdown_signals:
158
+ shutdown_trigger = await _install_signal_handler(lifespan_task_group)
159
+
160
+ await lifespan_task_group.start(lifespan.handle_lifespan)
161
+ await lifespan.wait_for_startup()
162
+
163
+ async with anyio.create_task_group() as server_task_group:
164
+ if sockets is None:
165
+ sockets = create_sockets(config)
166
+ for sock in sockets.insecure_sockets:
167
+ sock.listen(config.backlog)
168
+
169
+ listeners = []
170
+ binds = []
144
171
 
145
- async with anyio.create_task_group() as lifespan_task_group:
146
- if shutdown_trigger is None and handle_shutdown_signals:
147
- shutdown_trigger = await _install_signal_handler(lifespan_task_group)
148
-
149
- await lifespan_task_group.start(lifespan.handle_lifespan)
150
- await lifespan.wait_for_startup()
151
-
152
- async with anyio.create_task_group() as server_task_group:
153
- if sockets is None:
154
- sockets = create_sockets(config)
155
172
  for sock in sockets.insecure_sockets:
156
- sock.listen(config.backlog)
157
-
158
- listeners = []
159
- binds = []
160
-
161
- for sock in sockets.insecure_sockets:
162
- listeners.append(anyio._core._eventloop.get_async_backend().create_tcp_listener(sock)) # noqa
163
- bind = repr_socket_addr(sock.family, sock.getsockname())
164
- binds.append(f'http://{bind}')
165
- log.info('Running on http://%s (CTRL + C to quit)', bind)
166
-
167
- task_status.started(binds)
168
- try:
169
- async with anyio.create_task_group() as task_group:
170
- if shutdown_trigger is not None:
171
- task_group.start_soon(raise_shutdown, shutdown_trigger)
172
- task_group.start_soon(raise_shutdown, context.terminate.wait)
173
-
174
- task_group.start_soon(
175
- functools.partial(
176
- serve_listeners,
177
- functools.partial(TcpServer, app, config, context),
178
- listeners,
179
- handler_task_group=server_task_group,
180
- ),
181
- )
182
-
183
- await anyio.sleep_forever()
184
- except BaseExceptionGroup as error:
185
- _, other_errors = error.split((ShutdownError, KeyboardInterrupt)) # noqa
186
- if other_errors is not None:
187
- raise other_errors # noqa
188
- finally:
189
- await context.terminated.set()
190
- server_task_group.cancel_scope.deadline = anyio.current_time() + config.graceful_timeout
191
-
192
- await lifespan.wait_for_shutdown()
193
- lifespan_task_group.cancel_scope.cancel()
173
+ listeners.append(anyio._core._eventloop.get_async_backend().create_tcp_listener(sock)) # noqa
174
+ bind = repr_socket_addr(sock.family, sock.getsockname())
175
+ binds.append(f'http://{bind}')
176
+ log.info('Running on http://%s (CTRL + C to quit)', bind)
177
+
178
+ task_status.started(binds)
179
+ try:
180
+ async with anyio.create_task_group() as task_group:
181
+ if shutdown_trigger is not None:
182
+ task_group.start_soon(raise_shutdown, shutdown_trigger)
183
+ task_group.start_soon(raise_shutdown, context.terminate.wait)
184
+
185
+ task_group.start_soon(
186
+ functools.partial(
187
+ serve_listeners,
188
+ functools.partial(self._server_factory, app, context),
189
+ listeners,
190
+ handler_task_group=server_task_group,
191
+ ),
192
+ )
193
+
194
+ await anyio.sleep_forever()
195
+ except BaseExceptionGroup as error:
196
+ _, other_errors = error.split((ShutdownError, KeyboardInterrupt)) # noqa
197
+ if other_errors is not None:
198
+ raise other_errors # noqa
199
+ finally:
200
+ await context.terminated.set()
201
+ server_task_group.cancel_scope.deadline = anyio.current_time() + config.graceful_timeout
202
+
203
+ await lifespan.wait_for_shutdown()
204
+ lifespan_task_group.cancel_scope.cancel()
@@ -12,11 +12,11 @@ import typing as ta
12
12
  import anyio
13
13
 
14
14
  from .config import Config
15
+ from .default import serve
15
16
  from .sockets import Sockets
16
17
  from .sockets import create_sockets
17
18
  from .types import AsgiFramework
18
19
  from .types import wrap_app
19
- from .workers import serve
20
20
 
21
21
 
22
22
  async def check_multiprocess_shutdown_event(
@@ -6,6 +6,7 @@ import typing as ta
6
6
  import anyio.abc
7
7
 
8
8
  from omlish import check
9
+ from omlish import lang
9
10
 
10
11
  from .config import Config
11
12
  from .events import Closed
@@ -25,13 +26,18 @@ log = logging.getLogger(__name__)
25
26
  MAX_RECV = 2 ** 16
26
27
 
27
28
 
28
- class TcpServer:
29
+ class ServerFactory(lang.Func3[AppWrapper, WorkerContext, anyio.abc.SocketStream, 'Server']):
30
+ pass
31
+
32
+
33
+ class Server:
29
34
  def __init__(
30
35
  self,
31
36
  app: AppWrapper,
32
- config: Config,
33
37
  context: WorkerContext,
34
38
  stream: anyio.abc.SocketStream,
39
+ *,
40
+ config: Config,
35
41
  ) -> None:
36
42
  super().__init__()
37
43
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: omserv
3
- Version: 0.0.0.dev160
3
+ Version: 0.0.0.dev217
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.dev160
15
+ Requires-Dist: omlish==0.0.0.dev217
16
16
  Provides-Extra: all
17
17
  Requires-Dist: h11~=0.14; extra == "all"
18
18
  Requires-Dist: h2~=4.1; extra == "all"
@@ -9,32 +9,33 @@ omserv/apps/routes.py,sha256=shcN8qCSF2YoKal7nk-lemCAK3RX8MuHgNHhq_CTnh0,3762
9
9
  omserv/apps/sessions.py,sha256=glruQSbOSbCYLPp6nDRNSHCyp5hj4oiOPhh3R0F9BTM,1537
10
10
  omserv/apps/templates.py,sha256=PBRZHIF9UbnFnq-4EC6RmPeRkeH8lCBbpJkSdseHs6A,2125
11
11
  omserv/daemon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- omserv/nginx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- omserv/nginx/build.py,sha256=GdDOB3_yLIBKsm6DDcCv7FfajB5-G7x75JcFOgW9CVM,3145
14
- omserv/nginx/configs.py,sha256=4oQDcKJKIatRG621qiZCYTayJI3-vv63TtRCiUxEVWI,2008
12
+ omserv/nginx/__init__.py,sha256=2d63LCGFA2qS7gdl2nCvNPmQWXNICu19wZIihQJPHCs,48
13
+ omserv/nginx/build.py,sha256=2LzI5eM3U2V0CbUHEOHx4zwnMl5CU2lhu-odPrI7idU,3203
15
14
  omserv/nginx/logs.py,sha256=cODPsG1j3EQiXbb9SR20NpB9MjGdWN0ArFZ-TA9xf-c,1840
16
15
  omserv/nginx/stubstatus.py,sha256=_VnXZdXxSA7jIelYSwJLf9mOnt_UOvpWghAPWtlWSLw,1857
17
16
  omserv/nginx/patches/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
17
  omserv/nginx/patches/nginx-1.27.3_http_status.patch,sha256=bEDSczpBLcdjcBp_X1m73oxvt8KPeons7v_sUxqBSXM,4335
19
- omserv/node/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- omserv/node/models.py,sha256=EOaq-aW1rbGOzHTmTULhbVwV5j_burL07qh2QO9smdM,1273
21
- omserv/node/registry.py,sha256=y67VMowll9IuLiTVgauAcbP8-evFJNqpwocwwt7kZL4,3478
22
- omserv/node/sql.py,sha256=vy7RP50JiH3jQHMVa7Hxk0pFJK3QcbGeTvyNppB1W4I,2826
18
+ omserv/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ omserv/nodes/models.py,sha256=EOaq-aW1rbGOzHTmTULhbVwV5j_burL07qh2QO9smdM,1273
20
+ omserv/nodes/registry.py,sha256=y67VMowll9IuLiTVgauAcbP8-evFJNqpwocwwt7kZL4,3478
21
+ omserv/nodes/sql.py,sha256=vy7RP50JiH3jQHMVa7Hxk0pFJK3QcbGeTvyNppB1W4I,2826
23
22
  omserv/server/LICENSE,sha256=VKPNmbyrS9wcwcx20hBlVtLP01brb2dByHrWHeNLPag,1050
24
- omserv/server/__init__.py,sha256=7c9pUvoC-3lByJeGoQfAcp78JCZ9oA5wP0Bzpm87D7Y,372
23
+ omserv/server/__init__.py,sha256=jrFdrxfnQwjK5DW5y9w0alw6IKToGn2IQyZDhsuGHv4,372
25
24
  omserv/server/config.py,sha256=oGWL1kuk45bJ6sVr8n3ow5Q-1nz9EqByjoykU2iOHIY,1189
26
25
  omserv/server/debug.py,sha256=N7RI0Jj-ttmys3DJD0RREmGG5XZpTCp6y9Yu0x98Agg,299
26
+ omserv/server/default.py,sha256=hmfy--Q35QFMU8oTf4uHwVM2qBFG8mQDR9Wik2f1yZk,970
27
27
  omserv/server/events.py,sha256=VMr_rArsVjJYnyH9SqLWtOLUg18vSu1O0ep9gNBGR_c,1369
28
28
  omserv/server/headers.py,sha256=3H-NxMMQg5WuF5wF4AWFUEqkToh4NqNqHouavzbOQok,1188
29
+ omserv/server/inject.py,sha256=mD3MIDd44_nr1mIiDfjzgJ4DCBeD3JILN6STTDNReGc,454
29
30
  omserv/server/lifespans.py,sha256=kRVxDQM18jCBzRUpafyb69q_bGSCyxxjAtrkxjqcZdE,4607
30
- omserv/server/multiprocess.py,sha256=jKmQzj_Nrwxw2mM-Tf3XVf6vPYMbfyCkPOUWVe028Q8,4253
31
+ omserv/server/listener.py,sha256=t_wUlS6pXnRn-aEXV8D8jhWAeIlQ3a3Sx9i_0BOKV70,7033
32
+ omserv/server/multiprocess.py,sha256=qvNFQEMpTXXR5ACdja4JVC0HFgdvb8CL94iKLDYvZ48,4253
33
+ omserv/server/server.py,sha256=hgUTfZAUwF7V-HdsdV98KbYmE1IRKmEO3B1LlKTiPWc,5223
31
34
  omserv/server/sockets.py,sha256=lwqNP7URlp605ibsjHzp0pc-lyjcyTu-hD-uyojLUYk,3389
32
35
  omserv/server/ssl.py,sha256=gmB5ecM8Mck-YtGYF8pb2dwFdjABVGzERFCDzM9lBck,1483
33
36
  omserv/server/taskspawner.py,sha256=ljzF26UPtnp7GLAY_BvjzuwCoCO9aL7TKLwRNTmUy1M,3008
34
- omserv/server/tcpserver.py,sha256=akC-2WOhmoIiJBH0Ti0m1uK_sOTBYGie0CoRkEcUmkA,5082
35
37
  omserv/server/types.py,sha256=XXY5py8RYlEeD4FZrWNqSyX7DD-ffSlcG-T2s9BY9eI,2017
36
38
  omserv/server/workercontext.py,sha256=4rcLuGsyiU7URO7T_eHylOBPPNUS9C23QfEUVyJUtIY,1200
37
- omserv/server/workers.py,sha256=rdV8qEzWKMZ6HnN9nUoGS58U9LRsrsqOcAd_7yl73Y0,6586
38
39
  omserv/server/protocols/__init__.py,sha256=Ryu2PDZ1TUI6F2l-HBEYgyzZ7wHqE6VmjqnS0tIvmQI,47
39
40
  omserv/server/protocols/h11.py,sha256=_q_paD-ff0AWJEPaNK-6MUsQVtYRiALnWGwFyM3D0KU,11976
40
41
  omserv/server/protocols/h2.py,sha256=bC-qmRQqgLASL1DtF3UX1ozximHH4xtae1d_vN2PffY,15345
@@ -46,9 +47,9 @@ omserv/server/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
46
47
  omserv/server/streams/httpstream.py,sha256=0DeiAPLGbEGNa0fHTs8lUpi_CFZs4M5_QB-TiS8mobQ,8015
47
48
  omserv/server/streams/utils.py,sha256=aMOrqWIg_Hht5W4kLg3y7oR5AEkVvMrZhyjzo6U5owE,1527
48
49
  omserv/server/streams/wsstream.py,sha256=3Vyzox7dCE1tDSXjb6xBubWo41ZF9d38Hrsrlj6h1J8,15482
49
- omserv-0.0.0.dev160.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
50
- omserv-0.0.0.dev160.dist-info/METADATA,sha256=C8Z6OMHBH1YEbR4ZntGe0e7_cJNjrNDc1TE72QDWkmE,983
51
- omserv-0.0.0.dev160.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
52
- omserv-0.0.0.dev160.dist-info/entry_points.txt,sha256=ivSrdA_ahEbI-eVMu-XZS-z4VrnQISvpecIkOqC9zFM,35
53
- omserv-0.0.0.dev160.dist-info/top_level.txt,sha256=HXehpnxeKscKNULzKNzZ27oNawBrsh1PaNAirbX-XNA,7
54
- omserv-0.0.0.dev160.dist-info/RECORD,,
50
+ omserv-0.0.0.dev217.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
51
+ omserv-0.0.0.dev217.dist-info/METADATA,sha256=3cNDxomCex8x0ACSXMvztFjBrNg0xl_jn5U6G-Rc7WI,983
52
+ omserv-0.0.0.dev217.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
53
+ omserv-0.0.0.dev217.dist-info/entry_points.txt,sha256=ivSrdA_ahEbI-eVMu-XZS-z4VrnQISvpecIkOqC9zFM,35
54
+ omserv-0.0.0.dev217.dist-info/top_level.txt,sha256=HXehpnxeKscKNULzKNzZ27oNawBrsh1PaNAirbX-XNA,7
55
+ omserv-0.0.0.dev217.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
omserv/nginx/configs.py DELETED
@@ -1,86 +0,0 @@
1
- """
2
- TODO:
3
- - omnibus/jmespath
4
-
5
- https://nginx.org/en/docs/dev/development_guide.html
6
- https://nginx.org/en/docs/dev/development_guide.html#config_directives
7
- https://nginx.org/en/docs/example.html
8
-
9
- https://github.com/yandex/gixy
10
- """
11
- import dataclasses as dc
12
- import typing as ta
13
-
14
- from omlish import check
15
- from omlish import lang
16
- from omlish.text.indent import IndentWriter
17
-
18
-
19
- @dc.dataclass()
20
- class Items(lang.Final):
21
- lst: list['Item']
22
-
23
- @classmethod
24
- def of(cls, obj: ta.Any) -> 'Items':
25
- if isinstance(obj, Items):
26
- return obj
27
- return cls([Item.of(e) for e in check.isinstance(obj, list)])
28
-
29
-
30
- @dc.dataclass()
31
- class Item(lang.Final):
32
- name: str
33
- args: list[str] | None = None
34
- block: Items | None = None
35
-
36
- @classmethod
37
- def of(cls, obj: ta.Any) -> 'Item':
38
- if isinstance(obj, Item):
39
- return obj
40
- args = check.isinstance(obj, tuple)
41
- name, args = check.isinstance(args[0], str), args[1:]
42
- if args and not isinstance(args[-1], str):
43
- block, args = Items.of(args[-1]), args[:-1]
44
- else:
45
- block = None
46
- return Item(name, [check.isinstance(e, str) for e in args], block=block)
47
-
48
-
49
- def render(wr: IndentWriter, obj: ta.Any) -> None:
50
- if isinstance(obj, Item):
51
- wr.write(obj.name)
52
- for e in obj.args or ():
53
- wr.write(' ')
54
- wr.write(e)
55
- if obj.block:
56
- wr.write(' {\n')
57
- with wr.indent():
58
- render(wr, obj.block)
59
- wr.write('}\n')
60
- else:
61
- wr.write(';\n')
62
-
63
- elif isinstance(obj, Items):
64
- for e2 in obj.lst:
65
- render(wr, e2)
66
-
67
- else:
68
- raise TypeError(obj)
69
-
70
-
71
- def _main() -> None:
72
- conf = Items.of([
73
- ('user', 'www', 'www'),
74
- ('worker_processes', '2'),
75
- ('events', [
76
- ('worker_connections', '2000'),
77
- ]),
78
- ])
79
-
80
- wr = IndentWriter()
81
- render(wr, conf)
82
- print(wr.getvalue())
83
-
84
-
85
- if __name__ == '__main__':
86
- _main()
File without changes
File without changes
File without changes
File without changes