ominfra 0.0.0.dev156__py3-none-any.whl → 0.0.0.dev158__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.
- ominfra/clouds/aws/journald2aws/main.py +1 -1
- ominfra/journald/tailer.py +2 -2
- ominfra/manage/bootstrap_.py +1 -1
- ominfra/manage/commands/subprocess.py +4 -4
- ominfra/manage/deploy/apps.py +14 -15
- ominfra/manage/deploy/config.py +3 -0
- ominfra/manage/deploy/git.py +11 -27
- ominfra/manage/deploy/paths.py +48 -48
- ominfra/manage/deploy/specs.py +32 -0
- ominfra/manage/deploy/venvs.py +10 -5
- ominfra/manage/main.py +33 -4
- ominfra/manage/remote/spawning.py +4 -9
- ominfra/manage/system/packages.py +1 -1
- ominfra/pyremote.py +26 -26
- ominfra/scripts/journald2aws.py +469 -357
- ominfra/scripts/manage.py +2488 -1463
- ominfra/scripts/supervisor.py +385 -351
- ominfra/supervisor/configs.py +2 -0
- ominfra/supervisor/http.py +1 -1
- ominfra/supervisor/main.py +2 -2
- ominfra/supervisor/supervisor.py +2 -33
- ominfra/supervisor/utils/os.py +41 -0
- {ominfra-0.0.0.dev156.dist-info → ominfra-0.0.0.dev158.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev156.dist-info → ominfra-0.0.0.dev158.dist-info}/RECORD +28 -27
- {ominfra-0.0.0.dev156.dist-info → ominfra-0.0.0.dev158.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev156.dist-info → ominfra-0.0.0.dev158.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev156.dist-info → ominfra-0.0.0.dev158.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev156.dist-info → ominfra-0.0.0.dev158.dist-info}/top_level.txt +0 -0
ominfra/scripts/supervisor.py
CHANGED
@@ -142,9 +142,9 @@ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
|
142
142
|
ConfigMapping = ta.Mapping[str, ta.Any]
|
143
143
|
|
144
144
|
# ../../omlish/http/handlers.py
|
145
|
-
HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse']
|
145
|
+
HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.TypeAlias
|
146
146
|
|
147
|
-
# ../../omlish/http/
|
147
|
+
# ../../omlish/http/coro/server.py
|
148
148
|
CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
|
149
149
|
|
150
150
|
|
@@ -1697,8 +1697,8 @@ class _CachedNullary(_AbstractCachedNullary):
|
|
1697
1697
|
return self._value
|
1698
1698
|
|
1699
1699
|
|
1700
|
-
def cached_nullary(fn
|
1701
|
-
return _CachedNullary(fn)
|
1700
|
+
def cached_nullary(fn: CallableT) -> CallableT:
|
1701
|
+
return _CachedNullary(fn) # type: ignore
|
1702
1702
|
|
1703
1703
|
|
1704
1704
|
def static_init(fn: CallableT) -> CallableT:
|
@@ -2202,6 +2202,13 @@ json_dump_compact: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON
|
|
2202
2202
|
json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
|
2203
2203
|
|
2204
2204
|
|
2205
|
+
########################################
|
2206
|
+
# ../../../omlish/lite/logs.py
|
2207
|
+
|
2208
|
+
|
2209
|
+
log = logging.getLogger(__name__)
|
2210
|
+
|
2211
|
+
|
2205
2212
|
########################################
|
2206
2213
|
# ../../../omlish/lite/maybes.py
|
2207
2214
|
|
@@ -2486,6 +2493,116 @@ class Func3(ta.Generic[A0, A1, A2, T]):
|
|
2486
2493
|
return self.fn(a0, a1, a2)
|
2487
2494
|
|
2488
2495
|
|
2496
|
+
########################################
|
2497
|
+
# ../../../omlish/logs/filters.py
|
2498
|
+
|
2499
|
+
|
2500
|
+
class TidLogFilter(logging.Filter):
|
2501
|
+
def filter(self, record):
|
2502
|
+
record.tid = threading.get_native_id()
|
2503
|
+
return True
|
2504
|
+
|
2505
|
+
|
2506
|
+
########################################
|
2507
|
+
# ../../../omlish/logs/proxy.py
|
2508
|
+
|
2509
|
+
|
2510
|
+
class ProxyLogFilterer(logging.Filterer):
|
2511
|
+
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
2512
|
+
self._underlying = underlying
|
2513
|
+
|
2514
|
+
@property
|
2515
|
+
def underlying(self) -> logging.Filterer:
|
2516
|
+
return self._underlying
|
2517
|
+
|
2518
|
+
@property
|
2519
|
+
def filters(self):
|
2520
|
+
return self._underlying.filters
|
2521
|
+
|
2522
|
+
@filters.setter
|
2523
|
+
def filters(self, filters):
|
2524
|
+
self._underlying.filters = filters
|
2525
|
+
|
2526
|
+
def addFilter(self, filter): # noqa
|
2527
|
+
self._underlying.addFilter(filter)
|
2528
|
+
|
2529
|
+
def removeFilter(self, filter): # noqa
|
2530
|
+
self._underlying.removeFilter(filter)
|
2531
|
+
|
2532
|
+
def filter(self, record):
|
2533
|
+
return self._underlying.filter(record)
|
2534
|
+
|
2535
|
+
|
2536
|
+
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
2537
|
+
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
2538
|
+
ProxyLogFilterer.__init__(self, underlying)
|
2539
|
+
|
2540
|
+
_underlying: logging.Handler
|
2541
|
+
|
2542
|
+
@property
|
2543
|
+
def underlying(self) -> logging.Handler:
|
2544
|
+
return self._underlying
|
2545
|
+
|
2546
|
+
def get_name(self):
|
2547
|
+
return self._underlying.get_name()
|
2548
|
+
|
2549
|
+
def set_name(self, name):
|
2550
|
+
self._underlying.set_name(name)
|
2551
|
+
|
2552
|
+
@property
|
2553
|
+
def name(self):
|
2554
|
+
return self._underlying.name
|
2555
|
+
|
2556
|
+
@property
|
2557
|
+
def level(self):
|
2558
|
+
return self._underlying.level
|
2559
|
+
|
2560
|
+
@level.setter
|
2561
|
+
def level(self, level):
|
2562
|
+
self._underlying.level = level
|
2563
|
+
|
2564
|
+
@property
|
2565
|
+
def formatter(self):
|
2566
|
+
return self._underlying.formatter
|
2567
|
+
|
2568
|
+
@formatter.setter
|
2569
|
+
def formatter(self, formatter):
|
2570
|
+
self._underlying.formatter = formatter
|
2571
|
+
|
2572
|
+
def createLock(self):
|
2573
|
+
self._underlying.createLock()
|
2574
|
+
|
2575
|
+
def acquire(self):
|
2576
|
+
self._underlying.acquire()
|
2577
|
+
|
2578
|
+
def release(self):
|
2579
|
+
self._underlying.release()
|
2580
|
+
|
2581
|
+
def setLevel(self, level):
|
2582
|
+
self._underlying.setLevel(level)
|
2583
|
+
|
2584
|
+
def format(self, record):
|
2585
|
+
return self._underlying.format(record)
|
2586
|
+
|
2587
|
+
def emit(self, record):
|
2588
|
+
self._underlying.emit(record)
|
2589
|
+
|
2590
|
+
def handle(self, record):
|
2591
|
+
return self._underlying.handle(record)
|
2592
|
+
|
2593
|
+
def setFormatter(self, fmt):
|
2594
|
+
self._underlying.setFormatter(fmt)
|
2595
|
+
|
2596
|
+
def flush(self):
|
2597
|
+
self._underlying.flush()
|
2598
|
+
|
2599
|
+
def close(self):
|
2600
|
+
self._underlying.close()
|
2601
|
+
|
2602
|
+
def handleError(self, record):
|
2603
|
+
self._underlying.handleError(record)
|
2604
|
+
|
2605
|
+
|
2489
2606
|
########################################
|
2490
2607
|
# ../events.py
|
2491
2608
|
|
@@ -2858,6 +2975,44 @@ def decode_wait_status(sts: int) -> ta.Tuple[Rc, str]:
|
|
2858
2975
|
return Rc(-1), msg
|
2859
2976
|
|
2860
2977
|
|
2978
|
+
##
|
2979
|
+
|
2980
|
+
|
2981
|
+
class WaitedPid(ta.NamedTuple):
|
2982
|
+
pid: Pid
|
2983
|
+
sts: Rc
|
2984
|
+
|
2985
|
+
|
2986
|
+
def waitpid(
|
2987
|
+
*,
|
2988
|
+
log: ta.Optional[logging.Logger] = None,
|
2989
|
+
) -> ta.Optional[WaitedPid]:
|
2990
|
+
# Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
|
2991
|
+
# still a race condition here; we can get a sigchld while we're sitting in the waitpid call. However, AFAICT, if
|
2992
|
+
# waitpid is interrupted by SIGCHLD, as long as we call waitpid again (which happens every so often during the
|
2993
|
+
# normal course in the mainloop), we'll eventually reap the child that we tried to reap during the interrupted
|
2994
|
+
# call. At least on Linux, this appears to be true, or at least stopping 50 processes at once never left zombies
|
2995
|
+
# lying around.
|
2996
|
+
try:
|
2997
|
+
pid, sts = os.waitpid(-1, os.WNOHANG)
|
2998
|
+
|
2999
|
+
except OSError as exc:
|
3000
|
+
code = exc.args[0]
|
3001
|
+
|
3002
|
+
if code not in (errno.ECHILD, errno.EINTR):
|
3003
|
+
if log is not None:
|
3004
|
+
log.critical('waitpid error %r; a process may not be cleaned up properly', code)
|
3005
|
+
|
3006
|
+
if code == errno.EINTR:
|
3007
|
+
if log is not None:
|
3008
|
+
log.debug('EINTR during reap')
|
3009
|
+
|
3010
|
+
return None
|
3011
|
+
|
3012
|
+
else:
|
3013
|
+
return WaitedPid(pid, sts) # type: ignore
|
3014
|
+
|
3015
|
+
|
2861
3016
|
########################################
|
2862
3017
|
# ../utils/users.py
|
2863
3018
|
|
@@ -4635,333 +4790,63 @@ class Injection:
|
|
4635
4790
|
tag: ta.Any = None,
|
4636
4791
|
array: ta.Optional[bool] = None, # noqa
|
4637
4792
|
|
4638
|
-
to_fn: ta.Any = None,
|
4639
|
-
to_ctor: ta.Any = None,
|
4640
|
-
to_const: ta.Any = None,
|
4641
|
-
to_key: ta.Any = None,
|
4642
|
-
|
4643
|
-
singleton: bool = False,
|
4644
|
-
|
4645
|
-
eager: bool = False,
|
4646
|
-
) -> InjectorBindingOrBindings:
|
4647
|
-
return InjectorBinder.bind(
|
4648
|
-
obj,
|
4649
|
-
|
4650
|
-
key=key,
|
4651
|
-
tag=tag,
|
4652
|
-
array=array,
|
4653
|
-
|
4654
|
-
to_fn=to_fn,
|
4655
|
-
to_ctor=to_ctor,
|
4656
|
-
to_const=to_const,
|
4657
|
-
to_key=to_key,
|
4658
|
-
|
4659
|
-
singleton=singleton,
|
4660
|
-
|
4661
|
-
eager=eager,
|
4662
|
-
)
|
4663
|
-
|
4664
|
-
# helpers
|
4665
|
-
|
4666
|
-
@classmethod
|
4667
|
-
def bind_factory(
|
4668
|
-
cls,
|
4669
|
-
fn: ta.Callable[..., T],
|
4670
|
-
cls_: U,
|
4671
|
-
ann: ta.Any = None,
|
4672
|
-
) -> InjectorBindingOrBindings:
|
4673
|
-
return cls.bind(make_injector_factory(fn, cls_, ann))
|
4674
|
-
|
4675
|
-
@classmethod
|
4676
|
-
def bind_array(
|
4677
|
-
cls,
|
4678
|
-
obj: ta.Any = None,
|
4679
|
-
*,
|
4680
|
-
tag: ta.Any = None,
|
4681
|
-
) -> InjectorBindingOrBindings:
|
4682
|
-
return bind_injector_array(obj, tag=tag)
|
4683
|
-
|
4684
|
-
@classmethod
|
4685
|
-
def bind_array_type(
|
4686
|
-
cls,
|
4687
|
-
ele: ta.Union[InjectorKey, InjectorKeyCls],
|
4688
|
-
cls_: U,
|
4689
|
-
ann: ta.Any = None,
|
4690
|
-
) -> InjectorBindingOrBindings:
|
4691
|
-
return cls.bind(make_injector_array_type(ele, cls_, ann))
|
4692
|
-
|
4693
|
-
|
4694
|
-
inj = Injection
|
4695
|
-
|
4696
|
-
|
4697
|
-
########################################
|
4698
|
-
# ../../../omlish/lite/logs.py
|
4699
|
-
"""
|
4700
|
-
TODO:
|
4701
|
-
- translate json keys
|
4702
|
-
- debug
|
4703
|
-
"""
|
4704
|
-
|
4705
|
-
|
4706
|
-
log = logging.getLogger(__name__)
|
4707
|
-
|
4708
|
-
|
4709
|
-
##
|
4710
|
-
|
4711
|
-
|
4712
|
-
class TidLogFilter(logging.Filter):
|
4713
|
-
|
4714
|
-
def filter(self, record):
|
4715
|
-
record.tid = threading.get_native_id()
|
4716
|
-
return True
|
4717
|
-
|
4718
|
-
|
4719
|
-
##
|
4720
|
-
|
4721
|
-
|
4722
|
-
class JsonLogFormatter(logging.Formatter):
|
4723
|
-
|
4724
|
-
KEYS: ta.Mapping[str, bool] = {
|
4725
|
-
'name': False,
|
4726
|
-
'msg': False,
|
4727
|
-
'args': False,
|
4728
|
-
'levelname': False,
|
4729
|
-
'levelno': False,
|
4730
|
-
'pathname': False,
|
4731
|
-
'filename': False,
|
4732
|
-
'module': False,
|
4733
|
-
'exc_info': True,
|
4734
|
-
'exc_text': True,
|
4735
|
-
'stack_info': True,
|
4736
|
-
'lineno': False,
|
4737
|
-
'funcName': False,
|
4738
|
-
'created': False,
|
4739
|
-
'msecs': False,
|
4740
|
-
'relativeCreated': False,
|
4741
|
-
'thread': False,
|
4742
|
-
'threadName': False,
|
4743
|
-
'processName': False,
|
4744
|
-
'process': False,
|
4745
|
-
}
|
4746
|
-
|
4747
|
-
def format(self, record: logging.LogRecord) -> str:
|
4748
|
-
dct = {
|
4749
|
-
k: v
|
4750
|
-
for k, o in self.KEYS.items()
|
4751
|
-
for v in [getattr(record, k)]
|
4752
|
-
if not (o and v is None)
|
4753
|
-
}
|
4754
|
-
return json_dumps_compact(dct)
|
4755
|
-
|
4756
|
-
|
4757
|
-
##
|
4758
|
-
|
4759
|
-
|
4760
|
-
STANDARD_LOG_FORMAT_PARTS = [
|
4761
|
-
('asctime', '%(asctime)-15s'),
|
4762
|
-
('process', 'pid=%(process)-6s'),
|
4763
|
-
('thread', 'tid=%(thread)x'),
|
4764
|
-
('levelname', '%(levelname)s'),
|
4765
|
-
('name', '%(name)s'),
|
4766
|
-
('separator', '::'),
|
4767
|
-
('message', '%(message)s'),
|
4768
|
-
]
|
4769
|
-
|
4770
|
-
|
4771
|
-
class StandardLogFormatter(logging.Formatter):
|
4772
|
-
|
4773
|
-
@staticmethod
|
4774
|
-
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
4775
|
-
return ' '.join(v for k, v in parts)
|
4776
|
-
|
4777
|
-
converter = datetime.datetime.fromtimestamp # type: ignore
|
4778
|
-
|
4779
|
-
def formatTime(self, record, datefmt=None):
|
4780
|
-
ct = self.converter(record.created) # type: ignore
|
4781
|
-
if datefmt:
|
4782
|
-
return ct.strftime(datefmt) # noqa
|
4783
|
-
else:
|
4784
|
-
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
4785
|
-
return '%s.%03d' % (t, record.msecs) # noqa
|
4786
|
-
|
4787
|
-
|
4788
|
-
##
|
4789
|
-
|
4790
|
-
|
4791
|
-
class ProxyLogFilterer(logging.Filterer):
|
4792
|
-
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
4793
|
-
self._underlying = underlying
|
4794
|
-
|
4795
|
-
@property
|
4796
|
-
def underlying(self) -> logging.Filterer:
|
4797
|
-
return self._underlying
|
4798
|
-
|
4799
|
-
@property
|
4800
|
-
def filters(self):
|
4801
|
-
return self._underlying.filters
|
4802
|
-
|
4803
|
-
@filters.setter
|
4804
|
-
def filters(self, filters):
|
4805
|
-
self._underlying.filters = filters
|
4806
|
-
|
4807
|
-
def addFilter(self, filter): # noqa
|
4808
|
-
self._underlying.addFilter(filter)
|
4809
|
-
|
4810
|
-
def removeFilter(self, filter): # noqa
|
4811
|
-
self._underlying.removeFilter(filter)
|
4812
|
-
|
4813
|
-
def filter(self, record):
|
4814
|
-
return self._underlying.filter(record)
|
4815
|
-
|
4816
|
-
|
4817
|
-
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
4818
|
-
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
4819
|
-
ProxyLogFilterer.__init__(self, underlying)
|
4820
|
-
|
4821
|
-
_underlying: logging.Handler
|
4822
|
-
|
4823
|
-
@property
|
4824
|
-
def underlying(self) -> logging.Handler:
|
4825
|
-
return self._underlying
|
4826
|
-
|
4827
|
-
def get_name(self):
|
4828
|
-
return self._underlying.get_name()
|
4829
|
-
|
4830
|
-
def set_name(self, name):
|
4831
|
-
self._underlying.set_name(name)
|
4832
|
-
|
4833
|
-
@property
|
4834
|
-
def name(self):
|
4835
|
-
return self._underlying.name
|
4836
|
-
|
4837
|
-
@property
|
4838
|
-
def level(self):
|
4839
|
-
return self._underlying.level
|
4840
|
-
|
4841
|
-
@level.setter
|
4842
|
-
def level(self, level):
|
4843
|
-
self._underlying.level = level
|
4844
|
-
|
4845
|
-
@property
|
4846
|
-
def formatter(self):
|
4847
|
-
return self._underlying.formatter
|
4848
|
-
|
4849
|
-
@formatter.setter
|
4850
|
-
def formatter(self, formatter):
|
4851
|
-
self._underlying.formatter = formatter
|
4852
|
-
|
4853
|
-
def createLock(self):
|
4854
|
-
self._underlying.createLock()
|
4855
|
-
|
4856
|
-
def acquire(self):
|
4857
|
-
self._underlying.acquire()
|
4858
|
-
|
4859
|
-
def release(self):
|
4860
|
-
self._underlying.release()
|
4861
|
-
|
4862
|
-
def setLevel(self, level):
|
4863
|
-
self._underlying.setLevel(level)
|
4864
|
-
|
4865
|
-
def format(self, record):
|
4866
|
-
return self._underlying.format(record)
|
4867
|
-
|
4868
|
-
def emit(self, record):
|
4869
|
-
self._underlying.emit(record)
|
4870
|
-
|
4871
|
-
def handle(self, record):
|
4872
|
-
return self._underlying.handle(record)
|
4873
|
-
|
4874
|
-
def setFormatter(self, fmt):
|
4875
|
-
self._underlying.setFormatter(fmt)
|
4876
|
-
|
4877
|
-
def flush(self):
|
4878
|
-
self._underlying.flush()
|
4879
|
-
|
4880
|
-
def close(self):
|
4881
|
-
self._underlying.close()
|
4882
|
-
|
4883
|
-
def handleError(self, record):
|
4884
|
-
self._underlying.handleError(record)
|
4885
|
-
|
4886
|
-
|
4887
|
-
##
|
4888
|
-
|
4889
|
-
|
4890
|
-
class StandardLogHandler(ProxyLogHandler):
|
4891
|
-
pass
|
4892
|
-
|
4893
|
-
|
4894
|
-
##
|
4895
|
-
|
4896
|
-
|
4897
|
-
@contextlib.contextmanager
|
4898
|
-
def _locking_logging_module_lock() -> ta.Iterator[None]:
|
4899
|
-
if hasattr(logging, '_acquireLock'):
|
4900
|
-
logging._acquireLock() # noqa
|
4901
|
-
try:
|
4902
|
-
yield
|
4903
|
-
finally:
|
4904
|
-
logging._releaseLock() # type: ignore # noqa
|
4905
|
-
|
4906
|
-
elif hasattr(logging, '_lock'):
|
4907
|
-
# https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
|
4908
|
-
with logging._lock: # noqa
|
4909
|
-
yield
|
4910
|
-
|
4911
|
-
else:
|
4912
|
-
raise Exception("Can't find lock in logging module")
|
4913
|
-
|
4914
|
-
|
4915
|
-
def configure_standard_logging(
|
4916
|
-
level: ta.Union[int, str] = logging.INFO,
|
4917
|
-
*,
|
4918
|
-
json: bool = False,
|
4919
|
-
target: ta.Optional[logging.Logger] = None,
|
4920
|
-
force: bool = False,
|
4921
|
-
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
4922
|
-
) -> ta.Optional[StandardLogHandler]:
|
4923
|
-
with _locking_logging_module_lock():
|
4924
|
-
if target is None:
|
4925
|
-
target = logging.root
|
4926
|
-
|
4927
|
-
#
|
4928
|
-
|
4929
|
-
if not force:
|
4930
|
-
if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
|
4931
|
-
return None
|
4932
|
-
|
4933
|
-
#
|
4793
|
+
to_fn: ta.Any = None,
|
4794
|
+
to_ctor: ta.Any = None,
|
4795
|
+
to_const: ta.Any = None,
|
4796
|
+
to_key: ta.Any = None,
|
4934
4797
|
|
4935
|
-
|
4936
|
-
handler = handler_factory()
|
4937
|
-
else:
|
4938
|
-
handler = logging.StreamHandler()
|
4798
|
+
singleton: bool = False,
|
4939
4799
|
|
4940
|
-
|
4800
|
+
eager: bool = False,
|
4801
|
+
) -> InjectorBindingOrBindings:
|
4802
|
+
return InjectorBinder.bind(
|
4803
|
+
obj,
|
4941
4804
|
|
4942
|
-
|
4943
|
-
|
4944
|
-
|
4945
|
-
else:
|
4946
|
-
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
4947
|
-
handler.setFormatter(formatter)
|
4805
|
+
key=key,
|
4806
|
+
tag=tag,
|
4807
|
+
array=array,
|
4948
4808
|
|
4949
|
-
|
4809
|
+
to_fn=to_fn,
|
4810
|
+
to_ctor=to_ctor,
|
4811
|
+
to_const=to_const,
|
4812
|
+
to_key=to_key,
|
4950
4813
|
|
4951
|
-
|
4814
|
+
singleton=singleton,
|
4952
4815
|
|
4953
|
-
|
4816
|
+
eager=eager,
|
4817
|
+
)
|
4954
4818
|
|
4955
|
-
|
4819
|
+
# helpers
|
4956
4820
|
|
4957
|
-
|
4821
|
+
@classmethod
|
4822
|
+
def bind_factory(
|
4823
|
+
cls,
|
4824
|
+
fn: ta.Callable[..., T],
|
4825
|
+
cls_: U,
|
4826
|
+
ann: ta.Any = None,
|
4827
|
+
) -> InjectorBindingOrBindings:
|
4828
|
+
return cls.bind(make_injector_factory(fn, cls_, ann))
|
4958
4829
|
|
4959
|
-
|
4960
|
-
|
4830
|
+
@classmethod
|
4831
|
+
def bind_array(
|
4832
|
+
cls,
|
4833
|
+
obj: ta.Any = None,
|
4834
|
+
*,
|
4835
|
+
tag: ta.Any = None,
|
4836
|
+
) -> InjectorBindingOrBindings:
|
4837
|
+
return bind_injector_array(obj, tag=tag)
|
4961
4838
|
|
4962
|
-
|
4839
|
+
@classmethod
|
4840
|
+
def bind_array_type(
|
4841
|
+
cls,
|
4842
|
+
ele: ta.Union[InjectorKey, InjectorKeyCls],
|
4843
|
+
cls_: U,
|
4844
|
+
ann: ta.Any = None,
|
4845
|
+
) -> InjectorBindingOrBindings:
|
4846
|
+
return cls.bind(make_injector_array_type(ele, cls_, ann))
|
4963
4847
|
|
4964
|
-
|
4848
|
+
|
4849
|
+
inj = Injection
|
4965
4850
|
|
4966
4851
|
|
4967
4852
|
########################################
|
@@ -4971,6 +4856,7 @@ TODO:
|
|
4971
4856
|
- pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
|
4972
4857
|
- namedtuple
|
4973
4858
|
- literals
|
4859
|
+
- newtypes?
|
4974
4860
|
"""
|
4975
4861
|
|
4976
4862
|
|
@@ -5425,6 +5311,60 @@ def check_runtime_version() -> None:
|
|
5425
5311
|
raise OSError(f'Requires python {REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
|
5426
5312
|
|
5427
5313
|
|
5314
|
+
########################################
|
5315
|
+
# ../../../omlish/logs/json.py
|
5316
|
+
"""
|
5317
|
+
TODO:
|
5318
|
+
- translate json keys
|
5319
|
+
"""
|
5320
|
+
|
5321
|
+
|
5322
|
+
class JsonLogFormatter(logging.Formatter):
|
5323
|
+
KEYS: ta.Mapping[str, bool] = {
|
5324
|
+
'name': False,
|
5325
|
+
'msg': False,
|
5326
|
+
'args': False,
|
5327
|
+
'levelname': False,
|
5328
|
+
'levelno': False,
|
5329
|
+
'pathname': False,
|
5330
|
+
'filename': False,
|
5331
|
+
'module': False,
|
5332
|
+
'exc_info': True,
|
5333
|
+
'exc_text': True,
|
5334
|
+
'stack_info': True,
|
5335
|
+
'lineno': False,
|
5336
|
+
'funcName': False,
|
5337
|
+
'created': False,
|
5338
|
+
'msecs': False,
|
5339
|
+
'relativeCreated': False,
|
5340
|
+
'thread': False,
|
5341
|
+
'threadName': False,
|
5342
|
+
'processName': False,
|
5343
|
+
'process': False,
|
5344
|
+
}
|
5345
|
+
|
5346
|
+
def __init__(
|
5347
|
+
self,
|
5348
|
+
*args: ta.Any,
|
5349
|
+
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
5350
|
+
**kwargs: ta.Any,
|
5351
|
+
) -> None:
|
5352
|
+
super().__init__(*args, **kwargs)
|
5353
|
+
|
5354
|
+
if json_dumps is None:
|
5355
|
+
json_dumps = json_dumps_compact
|
5356
|
+
self._json_dumps = json_dumps
|
5357
|
+
|
5358
|
+
def format(self, record: logging.LogRecord) -> str:
|
5359
|
+
dct = {
|
5360
|
+
k: v
|
5361
|
+
for k, o in self.KEYS.items()
|
5362
|
+
for v in [getattr(record, k)]
|
5363
|
+
if not (o and v is None)
|
5364
|
+
}
|
5365
|
+
return self._json_dumps(dct)
|
5366
|
+
|
5367
|
+
|
5428
5368
|
########################################
|
5429
5369
|
# ../../../omlish/os/journald.py
|
5430
5370
|
|
@@ -5813,6 +5753,126 @@ class UnsupportedMethodHttpHandlerError(Exception):
|
|
5813
5753
|
pass
|
5814
5754
|
|
5815
5755
|
|
5756
|
+
########################################
|
5757
|
+
# ../../../omlish/logs/standard.py
|
5758
|
+
"""
|
5759
|
+
TODO:
|
5760
|
+
- structured
|
5761
|
+
- prefixed
|
5762
|
+
- debug
|
5763
|
+
"""
|
5764
|
+
|
5765
|
+
|
5766
|
+
##
|
5767
|
+
|
5768
|
+
|
5769
|
+
STANDARD_LOG_FORMAT_PARTS = [
|
5770
|
+
('asctime', '%(asctime)-15s'),
|
5771
|
+
('process', 'pid=%(process)-6s'),
|
5772
|
+
('thread', 'tid=%(thread)x'),
|
5773
|
+
('levelname', '%(levelname)s'),
|
5774
|
+
('name', '%(name)s'),
|
5775
|
+
('separator', '::'),
|
5776
|
+
('message', '%(message)s'),
|
5777
|
+
]
|
5778
|
+
|
5779
|
+
|
5780
|
+
class StandardLogFormatter(logging.Formatter):
|
5781
|
+
@staticmethod
|
5782
|
+
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
5783
|
+
return ' '.join(v for k, v in parts)
|
5784
|
+
|
5785
|
+
converter = datetime.datetime.fromtimestamp # type: ignore
|
5786
|
+
|
5787
|
+
def formatTime(self, record, datefmt=None):
|
5788
|
+
ct = self.converter(record.created) # type: ignore
|
5789
|
+
if datefmt:
|
5790
|
+
return ct.strftime(datefmt) # noqa
|
5791
|
+
else:
|
5792
|
+
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
5793
|
+
return '%s.%03d' % (t, record.msecs) # noqa
|
5794
|
+
|
5795
|
+
|
5796
|
+
##
|
5797
|
+
|
5798
|
+
|
5799
|
+
class StandardLogHandler(ProxyLogHandler):
|
5800
|
+
pass
|
5801
|
+
|
5802
|
+
|
5803
|
+
##
|
5804
|
+
|
5805
|
+
|
5806
|
+
@contextlib.contextmanager
|
5807
|
+
def _locking_logging_module_lock() -> ta.Iterator[None]:
|
5808
|
+
if hasattr(logging, '_acquireLock'):
|
5809
|
+
logging._acquireLock() # noqa
|
5810
|
+
try:
|
5811
|
+
yield
|
5812
|
+
finally:
|
5813
|
+
logging._releaseLock() # type: ignore # noqa
|
5814
|
+
|
5815
|
+
elif hasattr(logging, '_lock'):
|
5816
|
+
# https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
|
5817
|
+
with logging._lock: # noqa
|
5818
|
+
yield
|
5819
|
+
|
5820
|
+
else:
|
5821
|
+
raise Exception("Can't find lock in logging module")
|
5822
|
+
|
5823
|
+
|
5824
|
+
def configure_standard_logging(
|
5825
|
+
level: ta.Union[int, str] = logging.INFO,
|
5826
|
+
*,
|
5827
|
+
json: bool = False,
|
5828
|
+
target: ta.Optional[logging.Logger] = None,
|
5829
|
+
force: bool = False,
|
5830
|
+
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
5831
|
+
) -> ta.Optional[StandardLogHandler]:
|
5832
|
+
with _locking_logging_module_lock():
|
5833
|
+
if target is None:
|
5834
|
+
target = logging.root
|
5835
|
+
|
5836
|
+
#
|
5837
|
+
|
5838
|
+
if not force:
|
5839
|
+
if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
|
5840
|
+
return None
|
5841
|
+
|
5842
|
+
#
|
5843
|
+
|
5844
|
+
if handler_factory is not None:
|
5845
|
+
handler = handler_factory()
|
5846
|
+
else:
|
5847
|
+
handler = logging.StreamHandler()
|
5848
|
+
|
5849
|
+
#
|
5850
|
+
|
5851
|
+
formatter: logging.Formatter
|
5852
|
+
if json:
|
5853
|
+
formatter = JsonLogFormatter()
|
5854
|
+
else:
|
5855
|
+
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
5856
|
+
handler.setFormatter(formatter)
|
5857
|
+
|
5858
|
+
#
|
5859
|
+
|
5860
|
+
handler.addFilter(TidLogFilter())
|
5861
|
+
|
5862
|
+
#
|
5863
|
+
|
5864
|
+
target.addHandler(handler)
|
5865
|
+
|
5866
|
+
#
|
5867
|
+
|
5868
|
+
if level is not None:
|
5869
|
+
target.setLevel(level)
|
5870
|
+
|
5871
|
+
#
|
5872
|
+
|
5873
|
+
return StandardLogHandler(handler)
|
5874
|
+
|
5875
|
+
|
5816
5876
|
########################################
|
5817
5877
|
# ../configs.py
|
5818
5878
|
|
@@ -6049,6 +6109,8 @@ class ServerConfig:
|
|
6049
6109
|
|
6050
6110
|
groups: ta.Optional[ta.Sequence[ProcessGroupConfig]] = None
|
6051
6111
|
|
6112
|
+
group_config_dirs: ta.Optional[ta.Sequence[str]] = None
|
6113
|
+
|
6052
6114
|
@classmethod
|
6053
6115
|
def new(
|
6054
6116
|
cls,
|
@@ -6104,7 +6166,7 @@ def parse_logging_level(value: ta.Union[str, int]) -> int:
|
|
6104
6166
|
|
6105
6167
|
|
6106
6168
|
########################################
|
6107
|
-
# ../../../omlish/http/
|
6169
|
+
# ../../../omlish/http/coro/server.py
|
6108
6170
|
# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
6109
6171
|
# --------------------------------------------
|
6110
6172
|
#
|
@@ -6844,7 +6906,7 @@ class ProcessGroup(
|
|
6844
6906
|
|
6845
6907
|
|
6846
6908
|
########################################
|
6847
|
-
# ../../../omlish/
|
6909
|
+
# ../../../omlish/http/coro/fdio.py
|
6848
6910
|
|
6849
6911
|
|
6850
6912
|
class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
@@ -9054,7 +9116,7 @@ class Supervisor:
|
|
9054
9116
|
if depth >= 100:
|
9055
9117
|
return
|
9056
9118
|
|
9057
|
-
wp = waitpid()
|
9119
|
+
wp = waitpid(log=log)
|
9058
9120
|
|
9059
9121
|
if wp is None or not wp.pid:
|
9060
9122
|
return
|
@@ -9095,34 +9157,6 @@ class Supervisor:
|
|
9095
9157
|
self._event_callbacks.notify(event(this_tick, self))
|
9096
9158
|
|
9097
9159
|
|
9098
|
-
##
|
9099
|
-
|
9100
|
-
|
9101
|
-
class WaitedPid(ta.NamedTuple):
|
9102
|
-
pid: Pid
|
9103
|
-
sts: Rc
|
9104
|
-
|
9105
|
-
|
9106
|
-
def waitpid() -> ta.Optional[WaitedPid]:
|
9107
|
-
# Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
|
9108
|
-
# still a race condition here; we can get a sigchld while we're sitting in the waitpid call. However, AFAICT, if
|
9109
|
-
# waitpid is interrupted by SIGCHLD, as long as we call waitpid again (which happens every so often during the
|
9110
|
-
# normal course in the mainloop), we'll eventually reap the child that we tried to reap during the interrupted
|
9111
|
-
# call. At least on Linux, this appears to be true, or at least stopping 50 processes at once never left zombies
|
9112
|
-
# lying around.
|
9113
|
-
try:
|
9114
|
-
pid, sts = os.waitpid(-1, os.WNOHANG)
|
9115
|
-
except OSError as exc:
|
9116
|
-
code = exc.args[0]
|
9117
|
-
if code not in (errno.ECHILD, errno.EINTR):
|
9118
|
-
log.critical('waitpid error %r; a process may not be cleaned up properly', code)
|
9119
|
-
if code == errno.EINTR:
|
9120
|
-
log.debug('EINTR during reap')
|
9121
|
-
return None
|
9122
|
-
else:
|
9123
|
-
return WaitedPid(pid, sts) # type: ignore
|
9124
|
-
|
9125
|
-
|
9126
9160
|
########################################
|
9127
9161
|
# ../inject.py
|
9128
9162
|
|