ominfra 0.0.0.dev156__py3-none-any.whl → 0.0.0.dev158__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|