ominfra 0.0.0.dev216__py3-none-any.whl → 0.0.0.dev218__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.
@@ -4235,6 +4235,12 @@ def configure_standard_logging(
4235
4235
  ##
4236
4236
 
4237
4237
 
4238
+ # Valid channel type kwarg values:
4239
+ # - A special flag negative int
4240
+ # - A positive fd int
4241
+ # - A file-like object
4242
+ # - None
4243
+
4238
4244
  SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
4239
4245
  'pipe': subprocess.PIPE,
4240
4246
  'stdout': subprocess.STDOUT,
@@ -4280,6 +4286,25 @@ def subprocess_close(
4280
4286
  ##
4281
4287
 
4282
4288
 
4289
+ class VerboseCalledProcessError(subprocess.CalledProcessError):
4290
+ @classmethod
4291
+ def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
4292
+ return cls(
4293
+ e.returncode,
4294
+ e.cmd,
4295
+ output=e.output,
4296
+ stderr=e.stderr,
4297
+ )
4298
+
4299
+ def __str__(self) -> str:
4300
+ msg = super().__str__()
4301
+ if self.output is not None:
4302
+ msg += f' Output: {self.output!r}'
4303
+ if self.stderr is not None:
4304
+ msg += f' Stderr: {self.stderr!r}'
4305
+ return msg
4306
+
4307
+
4283
4308
  class BaseSubprocesses(abc.ABC): # noqa
4284
4309
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
4285
4310
 
@@ -4313,16 +4338,31 @@ class BaseSubprocesses(abc.ABC): # noqa
4313
4338
  if extra_env:
4314
4339
  self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
4315
4340
 
4341
+ #
4342
+
4316
4343
  if extra_env:
4317
4344
  env = {**(env if env is not None else os.environ), **extra_env}
4318
4345
 
4346
+ #
4347
+
4319
4348
  if quiet and 'stderr' not in kwargs:
4320
4349
  if self._log and not self._log.isEnabledFor(logging.DEBUG):
4321
4350
  kwargs['stderr'] = subprocess.DEVNULL
4322
4351
 
4352
+ for chk in ('stdout', 'stderr'):
4353
+ try:
4354
+ chv = kwargs[chk]
4355
+ except KeyError:
4356
+ continue
4357
+ kwargs[chk] = SUBPROCESS_CHANNEL_OPTION_VALUES.get(chv, chv)
4358
+
4359
+ #
4360
+
4323
4361
  if not shell:
4324
4362
  cmd = subprocess_maybe_shell_wrap_exec(*cmd)
4325
4363
 
4364
+ #
4365
+
4326
4366
  return cmd, dict(
4327
4367
  env=env,
4328
4368
  shell=shell,
@@ -4330,35 +4370,57 @@ class BaseSubprocesses(abc.ABC): # noqa
4330
4370
  )
4331
4371
 
4332
4372
  @contextlib.contextmanager
4333
- def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
4373
+ def wrap_call(
4374
+ self,
4375
+ *cmd: ta.Any,
4376
+ raise_verbose: bool = False,
4377
+ **kwargs: ta.Any,
4378
+ ) -> ta.Iterator[None]:
4334
4379
  start_time = time.time()
4335
4380
  try:
4336
4381
  if self._log:
4337
4382
  self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
4383
+
4338
4384
  yield
4339
4385
 
4340
4386
  except Exception as exc: # noqa
4341
4387
  if self._log:
4342
4388
  self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
4389
+
4390
+ if (
4391
+ raise_verbose and
4392
+ isinstance(exc, subprocess.CalledProcessError) and
4393
+ not isinstance(exc, VerboseCalledProcessError) and
4394
+ (exc.output is not None or exc.stderr is not None)
4395
+ ):
4396
+ raise VerboseCalledProcessError.from_std(exc) from exc
4397
+
4343
4398
  raise
4344
4399
 
4345
4400
  finally:
4346
4401
  end_time = time.time()
4347
4402
  elapsed_s = end_time - start_time
4403
+
4348
4404
  if self._log:
4349
- self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
4405
+ self._log.debug('Subprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
4350
4406
 
4351
4407
  @contextlib.contextmanager
4352
4408
  def prepare_and_wrap(
4353
4409
  self,
4354
4410
  *cmd: ta.Any,
4411
+ raise_verbose: bool = False,
4355
4412
  **kwargs: ta.Any,
4356
4413
  ) -> ta.Iterator[ta.Tuple[
4357
4414
  ta.Tuple[ta.Any, ...],
4358
4415
  ta.Dict[str, ta.Any],
4359
4416
  ]]:
4360
4417
  cmd, kwargs = self.prepare_args(*cmd, **kwargs)
4361
- with self.wrap_call(*cmd, **kwargs):
4418
+
4419
+ with self.wrap_call(
4420
+ *cmd,
4421
+ raise_verbose=raise_verbose,
4422
+ **kwargs,
4423
+ ):
4362
4424
  yield cmd, kwargs
4363
4425
 
4364
4426
  #
ominfra/scripts/manage.py CHANGED
@@ -8172,6 +8172,12 @@ def configure_standard_logging(
8172
8172
  ##
8173
8173
 
8174
8174
 
8175
+ # Valid channel type kwarg values:
8176
+ # - A special flag negative int
8177
+ # - A positive fd int
8178
+ # - A file-like object
8179
+ # - None
8180
+
8175
8181
  SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
8176
8182
  'pipe': subprocess.PIPE,
8177
8183
  'stdout': subprocess.STDOUT,
@@ -8217,6 +8223,25 @@ def subprocess_close(
8217
8223
  ##
8218
8224
 
8219
8225
 
8226
+ class VerboseCalledProcessError(subprocess.CalledProcessError):
8227
+ @classmethod
8228
+ def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
8229
+ return cls(
8230
+ e.returncode,
8231
+ e.cmd,
8232
+ output=e.output,
8233
+ stderr=e.stderr,
8234
+ )
8235
+
8236
+ def __str__(self) -> str:
8237
+ msg = super().__str__()
8238
+ if self.output is not None:
8239
+ msg += f' Output: {self.output!r}'
8240
+ if self.stderr is not None:
8241
+ msg += f' Stderr: {self.stderr!r}'
8242
+ return msg
8243
+
8244
+
8220
8245
  class BaseSubprocesses(abc.ABC): # noqa
8221
8246
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
8222
8247
 
@@ -8250,16 +8275,31 @@ class BaseSubprocesses(abc.ABC): # noqa
8250
8275
  if extra_env:
8251
8276
  self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
8252
8277
 
8278
+ #
8279
+
8253
8280
  if extra_env:
8254
8281
  env = {**(env if env is not None else os.environ), **extra_env}
8255
8282
 
8283
+ #
8284
+
8256
8285
  if quiet and 'stderr' not in kwargs:
8257
8286
  if self._log and not self._log.isEnabledFor(logging.DEBUG):
8258
8287
  kwargs['stderr'] = subprocess.DEVNULL
8259
8288
 
8289
+ for chk in ('stdout', 'stderr'):
8290
+ try:
8291
+ chv = kwargs[chk]
8292
+ except KeyError:
8293
+ continue
8294
+ kwargs[chk] = SUBPROCESS_CHANNEL_OPTION_VALUES.get(chv, chv)
8295
+
8296
+ #
8297
+
8260
8298
  if not shell:
8261
8299
  cmd = subprocess_maybe_shell_wrap_exec(*cmd)
8262
8300
 
8301
+ #
8302
+
8263
8303
  return cmd, dict(
8264
8304
  env=env,
8265
8305
  shell=shell,
@@ -8267,35 +8307,57 @@ class BaseSubprocesses(abc.ABC): # noqa
8267
8307
  )
8268
8308
 
8269
8309
  @contextlib.contextmanager
8270
- def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
8310
+ def wrap_call(
8311
+ self,
8312
+ *cmd: ta.Any,
8313
+ raise_verbose: bool = False,
8314
+ **kwargs: ta.Any,
8315
+ ) -> ta.Iterator[None]:
8271
8316
  start_time = time.time()
8272
8317
  try:
8273
8318
  if self._log:
8274
8319
  self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
8320
+
8275
8321
  yield
8276
8322
 
8277
8323
  except Exception as exc: # noqa
8278
8324
  if self._log:
8279
8325
  self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
8326
+
8327
+ if (
8328
+ raise_verbose and
8329
+ isinstance(exc, subprocess.CalledProcessError) and
8330
+ not isinstance(exc, VerboseCalledProcessError) and
8331
+ (exc.output is not None or exc.stderr is not None)
8332
+ ):
8333
+ raise VerboseCalledProcessError.from_std(exc) from exc
8334
+
8280
8335
  raise
8281
8336
 
8282
8337
  finally:
8283
8338
  end_time = time.time()
8284
8339
  elapsed_s = end_time - start_time
8340
+
8285
8341
  if self._log:
8286
- self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
8342
+ self._log.debug('Subprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
8287
8343
 
8288
8344
  @contextlib.contextmanager
8289
8345
  def prepare_and_wrap(
8290
8346
  self,
8291
8347
  *cmd: ta.Any,
8348
+ raise_verbose: bool = False,
8292
8349
  **kwargs: ta.Any,
8293
8350
  ) -> ta.Iterator[ta.Tuple[
8294
8351
  ta.Tuple[ta.Any, ...],
8295
8352
  ta.Dict[str, ta.Any],
8296
8353
  ]]:
8297
8354
  cmd, kwargs = self.prepare_args(*cmd, **kwargs)
8298
- with self.wrap_call(*cmd, **kwargs):
8355
+
8356
+ with self.wrap_call(
8357
+ *cmd,
8358
+ raise_verbose=raise_verbose,
8359
+ **kwargs,
8360
+ ):
8299
8361
  yield cmd, kwargs
8300
8362
 
8301
8363
  #
@@ -142,10 +142,6 @@ ConfigDataT = ta.TypeVar('ConfigDataT', bound='ConfigData')
142
142
  # ../../omlish/http/parsing.py
143
143
  HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
144
144
 
145
- # ../../omlish/lite/contextmanagers.py
146
- ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
147
- AsyncExitStackedT = ta.TypeVar('AsyncExitStackedT', bound='AsyncExitStacked')
148
-
149
145
  # ../../omlish/lite/inject.py
150
146
  U = ta.TypeVar('U')
151
147
  InjectorKeyCls = ta.Union[type, ta.NewType]
@@ -154,10 +150,11 @@ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
154
150
  InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
155
151
 
156
152
  # ../../omlish/sockets/handlers.py
157
- SocketHandlerFactory = ta.Callable[[SocketAddress, ta.BinaryIO, ta.BinaryIO], 'SocketHandler']
153
+ SocketHandler = ta.Callable[[SocketAddress, 'SocketIoPair'], None] # ta.TypeAlias
158
154
 
159
155
  # ../../omlish/http/handlers.py
160
156
  HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.TypeAlias
157
+ HttpHandlerResponseData = ta.Union[bytes, 'HttpHandlerResponseStreamedData'] # ta.TypeAlias # noqa
161
158
 
162
159
  # ../../omlish/http/coro/server.py
163
160
  CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
@@ -2812,8 +2809,7 @@ class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
2812
2809
  # ../../../omlish/sockets/addresses.py
2813
2810
  """
2814
2811
  TODO:
2815
- - SocketClientAddress family / tuple pairs
2816
- + codification of https://docs.python.org/3/library/socket.html#socket-families
2812
+ - codification of https://docs.python.org/3/library/socket.html#socket-families
2817
2813
  """
2818
2814
 
2819
2815
 
@@ -2839,11 +2835,16 @@ class SocketAddressInfo:
2839
2835
  sockaddr: SocketAddress
2840
2836
 
2841
2837
 
2838
+ class SocketFamilyAndAddress(ta.NamedTuple):
2839
+ family: socket.AddressFamily
2840
+ address: SocketAddress
2841
+
2842
+
2842
2843
  def get_best_socket_family(
2843
2844
  host: ta.Optional[str],
2844
2845
  port: ta.Union[str, int, None],
2845
2846
  family: ta.Union[int, socket.AddressFamily] = socket.AddressFamily.AF_UNSPEC,
2846
- ) -> ta.Tuple[socket.AddressFamily, SocketAddress]:
2847
+ ) -> SocketFamilyAndAddress:
2847
2848
  """https://github.com/python/cpython/commit/f289084c83190cc72db4a70c58f007ec62e75247"""
2848
2849
 
2849
2850
  infos = socket.getaddrinfo(
@@ -2854,7 +2855,80 @@ def get_best_socket_family(
2854
2855
  flags=socket.AI_PASSIVE,
2855
2856
  )
2856
2857
  ai = SocketAddressInfo(*next(iter(infos)))
2857
- return ai.family, ai.sockaddr
2858
+ return SocketFamilyAndAddress(ai.family, ai.sockaddr)
2859
+
2860
+
2861
+ class SocketAndAddress(ta.NamedTuple):
2862
+ socket: socket.socket
2863
+ address: SocketAddress
2864
+
2865
+
2866
+ ########################################
2867
+ # ../../../omlish/sockets/io.py
2868
+
2869
+
2870
+ ##
2871
+
2872
+
2873
+ class SocketWriter(io.BufferedIOBase):
2874
+ """
2875
+ Simple writable BufferedIOBase implementation for a socket
2876
+
2877
+ Does not hold data in a buffer, avoiding any need to call flush().
2878
+ """
2879
+
2880
+ def __init__(self, sock):
2881
+ super().__init__()
2882
+
2883
+ self._sock = sock
2884
+
2885
+ def writable(self):
2886
+ return True
2887
+
2888
+ def write(self, b):
2889
+ self._sock.sendall(b)
2890
+ with memoryview(b) as view:
2891
+ return view.nbytes
2892
+
2893
+ def fileno(self):
2894
+ return self._sock.fileno()
2895
+
2896
+
2897
+ class SocketIoPair(ta.NamedTuple):
2898
+ r: ta.BinaryIO
2899
+ w: ta.BinaryIO
2900
+
2901
+ @classmethod
2902
+ def from_socket(
2903
+ cls,
2904
+ sock: socket.socket,
2905
+ *,
2906
+ r_buf_size: int = -1,
2907
+ w_buf_size: int = 0,
2908
+ ) -> 'SocketIoPair':
2909
+ rf: ta.Any = sock.makefile('rb', r_buf_size)
2910
+
2911
+ if w_buf_size:
2912
+ wf: ta.Any = SocketWriter(sock)
2913
+ else:
2914
+ wf = sock.makefile('wb', w_buf_size)
2915
+
2916
+ return cls(rf, wf)
2917
+
2918
+
2919
+ ##
2920
+
2921
+
2922
+ def close_socket_immediately(sock: socket.socket) -> None:
2923
+ try:
2924
+ # Explicitly shutdown. socket.close() merely releases the socket and waits for GC to perform the actual close.
2925
+ sock.shutdown(socket.SHUT_WR)
2926
+
2927
+ except OSError:
2928
+ # Some platforms may raise ENOTCONN here
2929
+ pass
2930
+
2931
+ sock.close()
2858
2932
 
2859
2933
 
2860
2934
  ########################################
@@ -4426,116 +4500,6 @@ else:
4426
4500
  KqueueFdioPoller = None
4427
4501
 
4428
4502
 
4429
- ########################################
4430
- # ../../../omlish/lite/contextmanagers.py
4431
-
4432
-
4433
- ##
4434
-
4435
-
4436
- class ExitStacked:
4437
- _exit_stack: ta.Optional[contextlib.ExitStack] = None
4438
-
4439
- def __enter__(self: ExitStackedT) -> ExitStackedT:
4440
- check.state(self._exit_stack is None)
4441
- es = self._exit_stack = contextlib.ExitStack()
4442
- es.__enter__()
4443
- return self
4444
-
4445
- def __exit__(self, exc_type, exc_val, exc_tb):
4446
- if (es := self._exit_stack) is None:
4447
- return None
4448
- self._exit_contexts()
4449
- return es.__exit__(exc_type, exc_val, exc_tb)
4450
-
4451
- def _exit_contexts(self) -> None:
4452
- pass
4453
-
4454
- def _enter_context(self, cm: ta.ContextManager[T]) -> T:
4455
- es = check.not_none(self._exit_stack)
4456
- return es.enter_context(cm)
4457
-
4458
-
4459
- class AsyncExitStacked:
4460
- _exit_stack: ta.Optional[contextlib.AsyncExitStack] = None
4461
-
4462
- async def __aenter__(self: AsyncExitStackedT) -> AsyncExitStackedT:
4463
- check.state(self._exit_stack is None)
4464
- es = self._exit_stack = contextlib.AsyncExitStack()
4465
- await es.__aenter__()
4466
- return self
4467
-
4468
- async def __aexit__(self, exc_type, exc_val, exc_tb):
4469
- if (es := self._exit_stack) is None:
4470
- return None
4471
- await self._async_exit_contexts()
4472
- return await es.__aexit__(exc_type, exc_val, exc_tb)
4473
-
4474
- async def _async_exit_contexts(self) -> None:
4475
- pass
4476
-
4477
- def _enter_context(self, cm: ta.ContextManager[T]) -> T:
4478
- es = check.not_none(self._exit_stack)
4479
- return es.enter_context(cm)
4480
-
4481
- async def _enter_async_context(self, cm: ta.AsyncContextManager[T]) -> T:
4482
- es = check.not_none(self._exit_stack)
4483
- return await es.enter_async_context(cm)
4484
-
4485
-
4486
- ##
4487
-
4488
-
4489
- @contextlib.contextmanager
4490
- def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
4491
- try:
4492
- yield fn
4493
- finally:
4494
- fn()
4495
-
4496
-
4497
- @contextlib.asynccontextmanager
4498
- async def adefer(fn: ta.Callable) -> ta.AsyncGenerator[ta.Callable, None]:
4499
- try:
4500
- yield fn
4501
- finally:
4502
- await fn()
4503
-
4504
-
4505
- ##
4506
-
4507
-
4508
- @contextlib.contextmanager
4509
- def attr_setting(obj, attr, val, *, default=None): # noqa
4510
- not_set = object()
4511
- orig = getattr(obj, attr, not_set)
4512
- try:
4513
- setattr(obj, attr, val)
4514
- if orig is not not_set:
4515
- yield orig
4516
- else:
4517
- yield default
4518
- finally:
4519
- if orig is not_set:
4520
- delattr(obj, attr)
4521
- else:
4522
- setattr(obj, attr, orig)
4523
-
4524
-
4525
- ##
4526
-
4527
-
4528
- class aclosing(contextlib.AbstractAsyncContextManager): # noqa
4529
- def __init__(self, thing):
4530
- self.thing = thing
4531
-
4532
- async def __aenter__(self):
4533
- return self.thing
4534
-
4535
- async def __aexit__(self, *exc_info):
4536
- await self.thing.aclose()
4537
-
4538
-
4539
4503
  ########################################
4540
4504
  # ../../../omlish/lite/inject.py
4541
4505
 
@@ -6427,24 +6391,6 @@ def journald_log_handler_factory(
6427
6391
  ##
6428
6392
 
6429
6393
 
6430
- class SocketHandler(abc.ABC):
6431
- def __init__(
6432
- self,
6433
- client_address: SocketAddress,
6434
- rfile: ta.BinaryIO,
6435
- wfile: ta.BinaryIO,
6436
- ) -> None:
6437
- super().__init__()
6438
-
6439
- self._client_address = client_address
6440
- self._rfile = rfile
6441
- self._wfile = wfile
6442
-
6443
- @abc.abstractmethod
6444
- def handle(self) -> None:
6445
- raise NotImplementedError
6446
-
6447
-
6448
6394
  ########################################
6449
6395
  # ../configs.py
6450
6396
 
@@ -6865,6 +6811,9 @@ class SupervisorSetup(abc.ABC):
6865
6811
  # ../../../omlish/http/handlers.py
6866
6812
 
6867
6813
 
6814
+ ##
6815
+
6816
+
6868
6817
  @dc.dataclass(frozen=True)
6869
6818
  class HttpHandlerRequest:
6870
6819
  client_address: SocketAddress
@@ -6879,10 +6828,16 @@ class HttpHandlerResponse:
6879
6828
  status: ta.Union[http.HTTPStatus, int]
6880
6829
 
6881
6830
  headers: ta.Optional[ta.Mapping[str, str]] = None
6882
- data: ta.Optional[bytes] = None
6831
+ data: ta.Optional[HttpHandlerResponseData] = None
6883
6832
  close_connection: ta.Optional[bool] = None
6884
6833
 
6885
6834
 
6835
+ @dc.dataclass(frozen=True)
6836
+ class HttpHandlerResponseStreamedData:
6837
+ iter: ta.Iterable[bytes]
6838
+ length: ta.Optional[int] = None
6839
+
6840
+
6886
6841
  class HttpHandlerError(Exception):
6887
6842
  pass
6888
6843
 
@@ -7394,7 +7349,7 @@ class CoroHttpServer:
7394
7349
 
7395
7350
  message: ta.Optional[str] = None
7396
7351
  headers: ta.Optional[ta.Sequence['CoroHttpServer._Header']] = None
7397
- data: ta.Optional[bytes] = None
7352
+ data: ta.Optional[HttpHandlerResponseData] = None
7398
7353
  close_connection: ta.Optional[bool] = False
7399
7354
 
7400
7355
  def get_header(self, key: str) -> ta.Optional['CoroHttpServer._Header']:
@@ -7405,7 +7360,7 @@ class CoroHttpServer:
7405
7360
 
7406
7361
  #
7407
7362
 
7408
- def _build_response_bytes(self, a: _Response) -> bytes:
7363
+ def _build_response_head_bytes(self, a: _Response) -> bytes:
7409
7364
  out = io.BytesIO()
7410
7365
 
7411
7366
  if a.version >= HttpProtocolVersions.HTTP_1_0:
@@ -7420,11 +7375,22 @@ class CoroHttpServer:
7420
7375
 
7421
7376
  out.write(b'\r\n')
7422
7377
 
7423
- if a.data is not None:
7424
- out.write(a.data)
7425
-
7426
7378
  return out.getvalue()
7427
7379
 
7380
+ def _yield_response_data(self, a: _Response) -> ta.Iterator[bytes]:
7381
+ if a.data is None:
7382
+ return
7383
+
7384
+ elif isinstance(a.data, bytes):
7385
+ yield a.data
7386
+ return
7387
+
7388
+ elif isinstance(a.data, HttpHandlerResponseStreamedData):
7389
+ yield from a.data.iter
7390
+
7391
+ else:
7392
+ raise TypeError(a.data)
7393
+
7428
7394
  #
7429
7395
 
7430
7396
  DEFAULT_CONTENT_TYPE = 'text/plain'
@@ -7435,8 +7401,17 @@ class CoroHttpServer:
7435
7401
 
7436
7402
  if resp.get_header('Content-Type') is None:
7437
7403
  nh.append(self._Header('Content-Type', self._default_content_type))
7404
+
7438
7405
  if resp.data is not None and resp.get_header('Content-Length') is None:
7439
- nh.append(self._Header('Content-Length', str(len(resp.data))))
7406
+ cl: ta.Optional[int]
7407
+ if isinstance(resp.data, bytes):
7408
+ cl = len(resp.data)
7409
+ elif isinstance(resp.data, HttpHandlerResponseStreamedData):
7410
+ cl = resp.data.length
7411
+ else:
7412
+ raise TypeError(resp.data)
7413
+ if cl is not None:
7414
+ nh.append(self._Header('Content-Length', str(cl)))
7440
7415
 
7441
7416
  if nh:
7442
7417
  kw.update(headers=[*(resp.headers or []), *nh])
@@ -7610,9 +7585,13 @@ class CoroHttpServer:
7610
7585
 
7611
7586
  elif isinstance(o, self._Response):
7612
7587
  i = None
7588
+
7613
7589
  r = self._preprocess_response(o)
7614
- b = self._build_response_bytes(r)
7615
- check.none((yield self.WriteIo(b)))
7590
+ hb = self._build_response_head_bytes(r)
7591
+ check.none((yield self.WriteIo(hb)))
7592
+
7593
+ for b in self._yield_response_data(r):
7594
+ yield self.WriteIo(b)
7616
7595
 
7617
7596
  else:
7618
7597
  raise TypeError(o)
@@ -7731,27 +7710,20 @@ class CoroHttpServer:
7731
7710
  ##
7732
7711
 
7733
7712
 
7734
- class CoroHttpServerSocketHandler(SocketHandler):
7713
+ class CoroHttpServerSocketHandler: # SocketHandler
7735
7714
  def __init__(
7736
7715
  self,
7737
- client_address: SocketAddress,
7738
- rfile: ta.BinaryIO,
7739
- wfile: ta.BinaryIO,
7740
- *,
7741
7716
  server_factory: CoroHttpServerFactory,
7717
+ *,
7742
7718
  log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
7743
7719
  ) -> None:
7744
- super().__init__(
7745
- client_address,
7746
- rfile,
7747
- wfile,
7748
- )
7720
+ super().__init__()
7749
7721
 
7750
7722
  self._server_factory = server_factory
7751
7723
  self._log_handler = log_handler
7752
7724
 
7753
- def handle(self) -> None:
7754
- server = self._server_factory(self._client_address)
7725
+ def __call__(self, client_address: SocketAddress, fp: SocketIoPair) -> None:
7726
+ server = self._server_factory(client_address)
7755
7727
 
7756
7728
  gen = server.coro_handle()
7757
7729
 
@@ -7763,15 +7735,15 @@ class CoroHttpServerSocketHandler(SocketHandler):
7763
7735
  self._log_handler(server, o)
7764
7736
 
7765
7737
  elif isinstance(o, CoroHttpServer.ReadIo):
7766
- i = self._rfile.read(o.sz)
7738
+ i = fp.r.read(o.sz)
7767
7739
 
7768
7740
  elif isinstance(o, CoroHttpServer.ReadLineIo):
7769
- i = self._rfile.readline(o.sz)
7741
+ i = fp.r.readline(o.sz)
7770
7742
 
7771
7743
  elif isinstance(o, CoroHttpServer.WriteIo):
7772
7744
  i = None
7773
- self._wfile.write(o.data)
7774
- self._wfile.flush()
7745
+ fp.w.write(o.data)
7746
+ fp.w.flush()
7775
7747
 
7776
7748
  else:
7777
7749
  raise TypeError(o)
@@ -8877,7 +8849,7 @@ class HttpServer(HasDispatchers):
8877
8849
 
8878
8850
  self._conns: ta.List[CoroHttpServerConnectionFdioHandler] = []
8879
8851
 
8880
- exit_stack.enter_context(defer(self._server.close)) # noqa
8852
+ exit_stack.callback(self._server.close)
8881
8853
 
8882
8854
  def get_dispatchers(self) -> Dispatchers:
8883
8855
  l = []
@@ -10,7 +10,6 @@ from omlish.http.handlers import HttpHandlerRequest
10
10
  from omlish.http.handlers import HttpHandlerResponse
11
11
  from omlish.io.fdio.handlers import SocketFdioHandler
12
12
  from omlish.lite.check import check
13
- from omlish.lite.contextmanagers import defer
14
13
  from omlish.lite.json import JSON_PRETTY_KWARGS
15
14
  from omlish.sockets.addresses import SocketAddress
16
15
 
@@ -73,7 +72,7 @@ class HttpServer(HasDispatchers):
73
72
 
74
73
  self._conns: ta.List[CoroHttpServerConnectionFdioHandler] = []
75
74
 
76
- exit_stack.enter_context(defer(self._server.close)) # noqa
75
+ exit_stack.callback(self._server.close)
77
76
 
78
77
  def get_dispatchers(self) -> Dispatchers:
79
78
  l = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ominfra
3
- Version: 0.0.0.dev216
3
+ Version: 0.0.0.dev218
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ 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: omdev==0.0.0.dev216
16
- Requires-Dist: omlish==0.0.0.dev216
15
+ Requires-Dist: omdev==0.0.0.dev218
16
+ Requires-Dist: omlish==0.0.0.dev218
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -112,9 +112,9 @@ ominfra/manage/targets/connection.py,sha256=rVI1YJxFClcF-sdttqWyIz9_XjPI01GUdwxY
112
112
  ominfra/manage/targets/inject.py,sha256=P4597xWM-V3I_gCt2O71OLhYQkkXtuJvkYRsIbhhMcE,1561
113
113
  ominfra/manage/targets/targets.py,sha256=7GP6UAZyJFEhpkJN6UQdpr_WN3p7C76v-s445y-WB6U,1885
114
114
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
- ominfra/scripts/journald2aws.py,sha256=bFtGEkYYTfrwh_tP8yfdTrPwLvGnPIuJk9TXtoh2HTI,169937
116
- ominfra/scripts/manage.py,sha256=tLPoozkPT8SQqolTOPWvktxtGmpkcP2D1WveWl5bPVk,361314
117
- ominfra/scripts/supervisor.py,sha256=Laa7EM9tsLzjzb2Pkg2DVeUJX7OpsWLHuwlA9WP0cNM,295534
115
+ ominfra/scripts/journald2aws.py,sha256=CM1nZMpecXJpcg7icJUCJ-_DX-7CduQurxZQ7EmhQmA,171450
116
+ ominfra/scripts/manage.py,sha256=MaUur1_dDdMHt5YnE6Waxa_v70kydHzKCBHXAuTmKd0,362827
117
+ ominfra/scripts/supervisor.py,sha256=ejPO7r3Ta0RQbP28MyL8MJNS1jop6g8d9qdv-vJ0OYA,294602
118
118
  ominfra/supervisor/LICENSE.txt,sha256=ZrHY15PVR98y26Yg6iQfa-SXnUaYTDhrUsPVcEO5OKM,1874
119
119
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
120
120
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -125,7 +125,7 @@ ominfra/supervisor/events.py,sha256=XGrtzHr1xm0dwjz329fn9eR0_Ap-LQL6Sk8LJ8eVDEo,
125
125
  ominfra/supervisor/exceptions.py,sha256=Qbu211H3CLlSmi9LsSikOwrcL5HgJP9ugvcKWlGTAoI,750
126
126
  ominfra/supervisor/groups.py,sha256=MBbsbt8Zh_WEYkGOr1KXa82gnPVw9wPB2lAnqhugXSc,2457
127
127
  ominfra/supervisor/groupsimpl.py,sha256=PCDyc_Wc-pnvIj56_aEyiA5exCpK6n9iErqnJzO2rZk,2281
128
- ominfra/supervisor/http.py,sha256=Tl5eT6dyf4KuQXKbhGo1B6CFEzUDtPl_DZAuUFVD9zI,3453
128
+ ominfra/supervisor/http.py,sha256=P7afN223jeR_g36bPK-cWulnJo1wAeFUBWAaMKPycmw,3387
129
129
  ominfra/supervisor/inject.py,sha256=IeR-WKvK1sGNxMe6G2OBT5kSP7EUP5ipkDgcUFmDPCA,4838
130
130
  ominfra/supervisor/io.py,sha256=moaGNaPuYXEAUzLg8Qjo05DEIcOUNYUj8SSr8eT0d24,3198
131
131
  ominfra/supervisor/main.py,sha256=zCVuHZG2kGIPHwTLH4EUr5xpN4vJYessmKO2P0NE5RU,4381
@@ -156,9 +156,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
156
156
  ominfra/tailscale/cli.py,sha256=3FnJbgpLw6gInTfhERd1mDy9ijjMUGxkdYVo43Tnxx4,3555
157
157
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
158
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
159
- ominfra-0.0.0.dev216.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
- ominfra-0.0.0.dev216.dist-info/METADATA,sha256=Y0FMZP4JOwYVxzV7r07WQy-vBFgLUNg3W4903Ivusi4,731
161
- ominfra-0.0.0.dev216.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
162
- ominfra-0.0.0.dev216.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
163
- ominfra-0.0.0.dev216.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
164
- ominfra-0.0.0.dev216.dist-info/RECORD,,
159
+ ominfra-0.0.0.dev218.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
+ ominfra-0.0.0.dev218.dist-info/METADATA,sha256=CIoVjeFWhR47YXXqQ65hW7lHeJK6XpLd0jTGOcP6RA4,731
161
+ ominfra-0.0.0.dev218.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
162
+ ominfra-0.0.0.dev218.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
163
+ ominfra-0.0.0.dev218.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
164
+ ominfra-0.0.0.dev218.dist-info/RECORD,,