ominfra 0.0.0.dev124__py3-none-any.whl → 0.0.0.dev125__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/deploy/poly/_main.py +2 -6
- ominfra/deploy/poly/base.py +7 -8
- ominfra/scripts/supervisor.py +265 -148
- ominfra/supervisor/LICENSE.txt +19 -19
- ominfra/supervisor/context.py +5 -5
- ominfra/supervisor/dispatchers.py +5 -5
- ominfra/supervisor/events.py +3 -3
- ominfra/supervisor/groups.py +12 -18
- ominfra/supervisor/inject.py +10 -27
- ominfra/supervisor/main.py +30 -2
- ominfra/supervisor/process.py +8 -8
- ominfra/supervisor/supervisor.py +11 -16
- ominfra/supervisor/types.py +51 -6
- {ominfra-0.0.0.dev124.dist-info → ominfra-0.0.0.dev125.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev124.dist-info → ominfra-0.0.0.dev125.dist-info}/RECORD +19 -19
- {ominfra-0.0.0.dev124.dist-info → ominfra-0.0.0.dev125.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev124.dist-info → ominfra-0.0.0.dev125.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev124.dist-info → ominfra-0.0.0.dev125.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev124.dist-info → ominfra-0.0.0.dev125.dist-info}/top_level.txt +0 -0
ominfra/scripts/supervisor.py
CHANGED
@@ -4,6 +4,34 @@
|
|
4
4
|
# @omlish-script
|
5
5
|
# @omlish-amalg-output ../supervisor/main.py
|
6
6
|
# ruff: noqa: N802 UP006 UP007 UP012 UP036
|
7
|
+
# Supervisor is licensed under the following license:
|
8
|
+
#
|
9
|
+
# A copyright notice accompanies this license document that identifies the copyright holders.
|
10
|
+
#
|
11
|
+
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
12
|
+
# following conditions are met:
|
13
|
+
#
|
14
|
+
# 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the
|
15
|
+
# following disclaimer.
|
16
|
+
#
|
17
|
+
# 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the
|
18
|
+
# following disclaimer in the documentation and/or other materials provided with the distribution.
|
19
|
+
#
|
20
|
+
# 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without
|
21
|
+
# prior written permission from the copyright holders.
|
22
|
+
#
|
23
|
+
# 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed
|
24
|
+
# the files and the date of any change.
|
25
|
+
#
|
26
|
+
# Disclaimer
|
27
|
+
#
|
28
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
|
29
|
+
# NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
30
|
+
# EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
31
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
32
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
33
|
+
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
34
|
+
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
7
35
|
import abc
|
8
36
|
import base64
|
9
37
|
import collections.abc
|
@@ -74,6 +102,9 @@ T = ta.TypeVar('T')
|
|
74
102
|
SocketAddress = ta.Any
|
75
103
|
SocketHandlerFactory = ta.Callable[[SocketAddress, ta.BinaryIO, ta.BinaryIO], 'SocketHandler']
|
76
104
|
|
105
|
+
# ../events.py
|
106
|
+
EventCallback = ta.Callable[['Event'], None]
|
107
|
+
|
77
108
|
# ../../../omlish/lite/http/parsing.py
|
78
109
|
HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
79
110
|
|
@@ -92,12 +123,6 @@ HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse']
|
|
92
123
|
# ../../../omlish/lite/http/coroserver.py
|
93
124
|
CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
|
94
125
|
|
95
|
-
# ../context.py
|
96
|
-
ServerEpoch = ta.NewType('ServerEpoch', int)
|
97
|
-
|
98
|
-
# ../process.py
|
99
|
-
InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
|
100
|
-
|
101
126
|
|
102
127
|
########################################
|
103
128
|
# ../../../omdev/toml/parser.py
|
@@ -1537,6 +1562,18 @@ class SocketHandler(abc.ABC):
|
|
1537
1562
|
raise NotImplementedError
|
1538
1563
|
|
1539
1564
|
|
1565
|
+
########################################
|
1566
|
+
# ../../../omlish/lite/typing.py
|
1567
|
+
|
1568
|
+
|
1569
|
+
@dc.dataclass(frozen=True)
|
1570
|
+
class Func(ta.Generic[T]):
|
1571
|
+
fn: ta.Callable[..., T]
|
1572
|
+
|
1573
|
+
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
1574
|
+
return self.fn(*args, **kwargs)
|
1575
|
+
|
1576
|
+
|
1540
1577
|
########################################
|
1541
1578
|
# ../events.py
|
1542
1579
|
|
@@ -1551,9 +1588,6 @@ class Event(abc.ABC): # noqa
|
|
1551
1588
|
##
|
1552
1589
|
|
1553
1590
|
|
1554
|
-
EventCallback = ta.Callable[['Event'], None]
|
1555
|
-
|
1556
|
-
|
1557
1591
|
class EventCallbacks:
|
1558
1592
|
def __init__(self) -> None:
|
1559
1593
|
super().__init__()
|
@@ -2398,7 +2432,13 @@ class HttpRequestParser:
|
|
2398
2432
|
|
2399
2433
|
@dc.dataclass(frozen=True)
|
2400
2434
|
class InjectorKey(ta.Generic[T]):
|
2401
|
-
cls
|
2435
|
+
# Before PEP-560 typing.Generic was a metaclass with a __new__ that takes a 'cls' arg, so instantiating a dataclass
|
2436
|
+
# with kwargs (such as through dc.replace) causes `TypeError: __new__() got multiple values for argument 'cls'`.
|
2437
|
+
# See:
|
2438
|
+
# - https://github.com/python/cpython/commit/d911e40e788fb679723d78b6ea11cabf46caed5a
|
2439
|
+
# - https://gist.github.com/wrmsr/4468b86efe9f373b6b114bfe85b98fd3
|
2440
|
+
cls_: InjectorKeyCls
|
2441
|
+
|
2402
2442
|
tag: ta.Any = None
|
2403
2443
|
array: bool = False
|
2404
2444
|
|
@@ -2519,14 +2559,14 @@ class FnInjectorProvider(InjectorProvider):
|
|
2519
2559
|
|
2520
2560
|
@dc.dataclass(frozen=True)
|
2521
2561
|
class CtorInjectorProvider(InjectorProvider):
|
2522
|
-
|
2562
|
+
cls_: type
|
2523
2563
|
|
2524
2564
|
def __post_init__(self) -> None:
|
2525
|
-
check_isinstance(self.
|
2565
|
+
check_isinstance(self.cls_, type)
|
2526
2566
|
|
2527
2567
|
def provider_fn(self) -> InjectorProviderFn:
|
2528
2568
|
def pfn(i: Injector) -> ta.Any:
|
2529
|
-
return i.inject(self.
|
2569
|
+
return i.inject(self.cls_)
|
2530
2570
|
|
2531
2571
|
return pfn
|
2532
2572
|
|
@@ -2675,19 +2715,42 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
|
|
2675
2715
|
# inspection
|
2676
2716
|
|
2677
2717
|
|
2678
|
-
|
2718
|
+
# inspect.signature(eval_str=True) was added in 3.10 and we have to support 3.8, so we have to get_type_hints to eval
|
2719
|
+
# str annotations *in addition to* getting the signature for parameter information.
|
2720
|
+
class _InjectionInspection(ta.NamedTuple):
|
2721
|
+
signature: inspect.Signature
|
2722
|
+
type_hints: ta.Mapping[str, ta.Any]
|
2723
|
+
|
2679
2724
|
|
2725
|
+
_INJECTION_INSPECTION_CACHE: ta.MutableMapping[ta.Any, _InjectionInspection] = weakref.WeakKeyDictionary()
|
2680
2726
|
|
2681
|
-
|
2727
|
+
|
2728
|
+
def _do_injection_inspect(obj: ta.Any) -> _InjectionInspection:
|
2729
|
+
uw = obj
|
2730
|
+
while True:
|
2731
|
+
if isinstance(uw, functools.partial):
|
2732
|
+
uw = uw.func
|
2733
|
+
else:
|
2734
|
+
if (uw2 := inspect.unwrap(uw)) is uw:
|
2735
|
+
break
|
2736
|
+
uw = uw2
|
2737
|
+
|
2738
|
+
return _InjectionInspection(
|
2739
|
+
inspect.signature(obj),
|
2740
|
+
ta.get_type_hints(uw),
|
2741
|
+
)
|
2742
|
+
|
2743
|
+
|
2744
|
+
def _injection_inspect(obj: ta.Any) -> _InjectionInspection:
|
2682
2745
|
try:
|
2683
|
-
return
|
2746
|
+
return _INJECTION_INSPECTION_CACHE[obj]
|
2684
2747
|
except TypeError:
|
2685
|
-
return
|
2748
|
+
return _do_injection_inspect(obj)
|
2686
2749
|
except KeyError:
|
2687
2750
|
pass
|
2688
|
-
|
2689
|
-
|
2690
|
-
return
|
2751
|
+
insp = _do_injection_inspect(obj)
|
2752
|
+
_INJECTION_INSPECTION_CACHE[obj] = insp
|
2753
|
+
return insp
|
2691
2754
|
|
2692
2755
|
|
2693
2756
|
class InjectionKwarg(ta.NamedTuple):
|
@@ -2708,20 +2771,20 @@ def build_injection_kwargs_target(
|
|
2708
2771
|
skip_kwargs: ta.Optional[ta.Iterable[ta.Any]] = None,
|
2709
2772
|
raw_optional: bool = False,
|
2710
2773
|
) -> InjectionKwargsTarget:
|
2711
|
-
|
2774
|
+
insp = _injection_inspect(obj)
|
2712
2775
|
|
2713
2776
|
seen: ta.Set[InjectorKey] = set(map(as_injector_key, skip_kwargs)) if skip_kwargs is not None else set()
|
2714
2777
|
kws: ta.List[InjectionKwarg] = []
|
2715
|
-
for p in list(
|
2778
|
+
for p in list(insp.signature.parameters.values())[skip_args:]:
|
2716
2779
|
if p.annotation is inspect.Signature.empty:
|
2717
2780
|
if p.default is not inspect.Parameter.empty:
|
2718
2781
|
raise KeyError(f'{obj}, {p.name}')
|
2719
2782
|
continue
|
2720
2783
|
|
2721
2784
|
if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
|
2722
|
-
raise TypeError(
|
2785
|
+
raise TypeError(insp)
|
2723
2786
|
|
2724
|
-
ann = p.annotation
|
2787
|
+
ann = insp.type_hints.get(p.name, p.annotation)
|
2725
2788
|
if (
|
2726
2789
|
not raw_optional and
|
2727
2790
|
is_optional_alias(ann)
|
@@ -2746,6 +2809,66 @@ def build_injection_kwargs_target(
|
|
2746
2809
|
)
|
2747
2810
|
|
2748
2811
|
|
2812
|
+
###
|
2813
|
+
# injector
|
2814
|
+
|
2815
|
+
|
2816
|
+
_INJECTOR_INJECTOR_KEY: InjectorKey[Injector] = InjectorKey(Injector)
|
2817
|
+
|
2818
|
+
|
2819
|
+
class _Injector(Injector):
|
2820
|
+
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
2821
|
+
super().__init__()
|
2822
|
+
|
2823
|
+
self._bs = check_isinstance(bs, InjectorBindings)
|
2824
|
+
self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
|
2825
|
+
|
2826
|
+
self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
|
2827
|
+
|
2828
|
+
if _INJECTOR_INJECTOR_KEY in self._pfm:
|
2829
|
+
raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
|
2830
|
+
|
2831
|
+
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
2832
|
+
key = as_injector_key(key)
|
2833
|
+
|
2834
|
+
if key == _INJECTOR_INJECTOR_KEY:
|
2835
|
+
return Maybe.just(self)
|
2836
|
+
|
2837
|
+
fn = self._pfm.get(key)
|
2838
|
+
if fn is not None:
|
2839
|
+
return Maybe.just(fn(self))
|
2840
|
+
|
2841
|
+
if self._p is not None:
|
2842
|
+
pv = self._p.try_provide(key)
|
2843
|
+
if pv is not None:
|
2844
|
+
return Maybe.empty()
|
2845
|
+
|
2846
|
+
return Maybe.empty()
|
2847
|
+
|
2848
|
+
def provide(self, key: ta.Any) -> ta.Any:
|
2849
|
+
v = self.try_provide(key)
|
2850
|
+
if v.present:
|
2851
|
+
return v.must()
|
2852
|
+
raise UnboundInjectorKeyError(key)
|
2853
|
+
|
2854
|
+
def provide_kwargs(self, obj: ta.Any) -> ta.Mapping[str, ta.Any]:
|
2855
|
+
kt = build_injection_kwargs_target(obj)
|
2856
|
+
ret: ta.Dict[str, ta.Any] = {}
|
2857
|
+
for kw in kt.kwargs:
|
2858
|
+
if kw.has_default:
|
2859
|
+
if not (mv := self.try_provide(kw.key)).present:
|
2860
|
+
continue
|
2861
|
+
v = mv.must()
|
2862
|
+
else:
|
2863
|
+
v = self.provide(kw.key)
|
2864
|
+
ret[kw.name] = v
|
2865
|
+
return ret
|
2866
|
+
|
2867
|
+
def inject(self, obj: ta.Any) -> ta.Any:
|
2868
|
+
kws = self.provide_kwargs(obj)
|
2869
|
+
return obj(**kws)
|
2870
|
+
|
2871
|
+
|
2749
2872
|
###
|
2750
2873
|
# binder
|
2751
2874
|
|
@@ -2795,7 +2918,7 @@ class InjectorBinder:
|
|
2795
2918
|
to_key: ta.Any = None,
|
2796
2919
|
|
2797
2920
|
singleton: bool = False,
|
2798
|
-
) ->
|
2921
|
+
) -> InjectorBindingOrBindings:
|
2799
2922
|
if obj is None or obj is inspect.Parameter.empty:
|
2800
2923
|
raise TypeError(obj)
|
2801
2924
|
if isinstance(obj, cls._BANNED_BIND_TYPES):
|
@@ -2825,8 +2948,8 @@ class InjectorBinder:
|
|
2825
2948
|
elif cls._is_fn(obj) and not has_to:
|
2826
2949
|
to_fn = obj
|
2827
2950
|
if key is None:
|
2828
|
-
|
2829
|
-
key_cls = check_valid_injector_key_cls(
|
2951
|
+
insp = _injection_inspect(obj)
|
2952
|
+
key_cls: ta.Any = check_valid_injector_key_cls(check_not_none(insp.type_hints.get('return')))
|
2830
2953
|
key = InjectorKey(key_cls)
|
2831
2954
|
else:
|
2832
2955
|
if to_const is not None:
|
@@ -2878,67 +3001,21 @@ class InjectorBinder:
|
|
2878
3001
|
|
2879
3002
|
|
2880
3003
|
###
|
2881
|
-
#
|
2882
|
-
|
2883
|
-
|
2884
|
-
_INJECTOR_INJECTOR_KEY: InjectorKey[Injector] = InjectorKey(Injector)
|
2885
|
-
|
2886
|
-
|
2887
|
-
class _Injector(Injector):
|
2888
|
-
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
2889
|
-
super().__init__()
|
2890
|
-
|
2891
|
-
self._bs = check_isinstance(bs, InjectorBindings)
|
2892
|
-
self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
|
2893
|
-
|
2894
|
-
self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
|
2895
|
-
|
2896
|
-
if _INJECTOR_INJECTOR_KEY in self._pfm:
|
2897
|
-
raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
|
2898
|
-
|
2899
|
-
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
2900
|
-
key = as_injector_key(key)
|
2901
|
-
|
2902
|
-
if key == _INJECTOR_INJECTOR_KEY:
|
2903
|
-
return Maybe.just(self)
|
2904
|
-
|
2905
|
-
fn = self._pfm.get(key)
|
2906
|
-
if fn is not None:
|
2907
|
-
return Maybe.just(fn(self))
|
2908
|
-
|
2909
|
-
if self._p is not None:
|
2910
|
-
pv = self._p.try_provide(key)
|
2911
|
-
if pv is not None:
|
2912
|
-
return Maybe.empty()
|
2913
|
-
|
2914
|
-
return Maybe.empty()
|
2915
|
-
|
2916
|
-
def provide(self, key: ta.Any) -> ta.Any:
|
2917
|
-
v = self.try_provide(key)
|
2918
|
-
if v.present:
|
2919
|
-
return v.must()
|
2920
|
-
raise UnboundInjectorKeyError(key)
|
3004
|
+
# injection helpers
|
2921
3005
|
|
2922
|
-
def provide_kwargs(self, obj: ta.Any) -> ta.Mapping[str, ta.Any]:
|
2923
|
-
kt = build_injection_kwargs_target(obj)
|
2924
|
-
ret: ta.Dict[str, ta.Any] = {}
|
2925
|
-
for kw in kt.kwargs:
|
2926
|
-
if kw.has_default:
|
2927
|
-
if not (mv := self.try_provide(kw.key)).present:
|
2928
|
-
continue
|
2929
|
-
v = mv.must()
|
2930
|
-
else:
|
2931
|
-
v = self.provide(kw.key)
|
2932
|
-
ret[kw.name] = v
|
2933
|
-
return ret
|
2934
3006
|
|
2935
|
-
|
2936
|
-
|
2937
|
-
|
3007
|
+
def make_injector_factory(
|
3008
|
+
factory_cls: ta.Any,
|
3009
|
+
factory_fn: ta.Callable[..., T],
|
3010
|
+
) -> ta.Callable[..., Func[T]]:
|
3011
|
+
def outer(injector: Injector) -> factory_cls:
|
3012
|
+
def inner(*args, **kwargs):
|
3013
|
+
return injector.inject(functools.partial(factory_fn, *args, **kwargs))
|
3014
|
+
return Func(inner)
|
3015
|
+
return outer
|
2938
3016
|
|
2939
3017
|
|
2940
|
-
|
2941
|
-
# injection helpers
|
3018
|
+
##
|
2942
3019
|
|
2943
3020
|
|
2944
3021
|
class Injection:
|
@@ -2969,6 +3046,12 @@ class Injection:
|
|
2969
3046
|
def override(cls, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
2970
3047
|
return injector_override(p, *args)
|
2971
3048
|
|
3049
|
+
# injector
|
3050
|
+
|
3051
|
+
@classmethod
|
3052
|
+
def create_injector(cls, *args: InjectorBindingOrBindings, p: ta.Optional[Injector] = None) -> Injector:
|
3053
|
+
return _Injector(as_injector_bindings(*args), p)
|
3054
|
+
|
2972
3055
|
# binder
|
2973
3056
|
|
2974
3057
|
@classmethod
|
@@ -2986,7 +3069,7 @@ class Injection:
|
|
2986
3069
|
to_key: ta.Any = None,
|
2987
3070
|
|
2988
3071
|
singleton: bool = False,
|
2989
|
-
) ->
|
3072
|
+
) -> InjectorBindingOrBindings:
|
2990
3073
|
return InjectorBinder.bind(
|
2991
3074
|
obj,
|
2992
3075
|
|
@@ -3002,11 +3085,15 @@ class Injection:
|
|
3002
3085
|
singleton=singleton,
|
3003
3086
|
)
|
3004
3087
|
|
3005
|
-
#
|
3088
|
+
# helpers
|
3006
3089
|
|
3007
3090
|
@classmethod
|
3008
|
-
def
|
3009
|
-
|
3091
|
+
def bind_factory(
|
3092
|
+
cls,
|
3093
|
+
factory_cls: ta.Any,
|
3094
|
+
factory_fn: ta.Callable[..., T],
|
3095
|
+
) -> InjectorBindingOrBindings:
|
3096
|
+
return cls.bind(make_injector_factory(factory_cls, factory_fn))
|
3010
3097
|
|
3011
3098
|
|
3012
3099
|
inj = Injection
|
@@ -4790,7 +4877,7 @@ class CoroHttpServerSocketHandler(SocketHandler):
|
|
4790
4877
|
# ../types.py
|
4791
4878
|
|
4792
4879
|
|
4793
|
-
class
|
4880
|
+
class ServerContext(abc.ABC):
|
4794
4881
|
@property
|
4795
4882
|
@abc.abstractmethod
|
4796
4883
|
def config(self) -> ServerConfig:
|
@@ -4807,12 +4894,24 @@ class AbstractServerContext(abc.ABC):
|
|
4807
4894
|
|
4808
4895
|
@property
|
4809
4896
|
@abc.abstractmethod
|
4810
|
-
def pid_history(self) -> ta.Dict[int, '
|
4897
|
+
def pid_history(self) -> ta.Dict[int, 'Process']:
|
4811
4898
|
raise NotImplementedError
|
4812
4899
|
|
4813
4900
|
|
4901
|
+
# class Dispatcher(abc.ABC):
|
4902
|
+
# pass
|
4903
|
+
#
|
4904
|
+
#
|
4905
|
+
# class OutputDispatcher(Dispatcher, abc.ABC):
|
4906
|
+
# pass
|
4907
|
+
#
|
4908
|
+
#
|
4909
|
+
# class InputDispatcher(Dispatcher, abc.ABC):
|
4910
|
+
# pass
|
4911
|
+
|
4912
|
+
|
4814
4913
|
@functools.total_ordering
|
4815
|
-
class
|
4914
|
+
class Process(abc.ABC):
|
4816
4915
|
@property
|
4817
4916
|
@abc.abstractmethod
|
4818
4917
|
def pid(self) -> int:
|
@@ -4831,7 +4930,7 @@ class AbstractSubprocess(abc.ABC):
|
|
4831
4930
|
|
4832
4931
|
@property
|
4833
4932
|
@abc.abstractmethod
|
4834
|
-
def context(self) ->
|
4933
|
+
def context(self) -> ServerContext:
|
4835
4934
|
raise NotImplementedError
|
4836
4935
|
|
4837
4936
|
@abc.abstractmethod
|
@@ -4867,12 +4966,12 @@ class AbstractSubprocess(abc.ABC):
|
|
4867
4966
|
raise NotImplementedError
|
4868
4967
|
|
4869
4968
|
@abc.abstractmethod
|
4870
|
-
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: #
|
4969
|
+
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: # Dispatcher]:
|
4871
4970
|
raise NotImplementedError
|
4872
4971
|
|
4873
4972
|
|
4874
4973
|
@functools.total_ordering
|
4875
|
-
class
|
4974
|
+
class ProcessGroup(abc.ABC):
|
4876
4975
|
@property
|
4877
4976
|
@abc.abstractmethod
|
4878
4977
|
def config(self) -> ProcessGroupConfig:
|
@@ -4884,12 +4983,48 @@ class AbstractProcessGroup(abc.ABC):
|
|
4884
4983
|
def __eq__(self, other):
|
4885
4984
|
return self.config.priority == other.config.priority
|
4886
4985
|
|
4986
|
+
@abc.abstractmethod
|
4987
|
+
def transition(self) -> None:
|
4988
|
+
raise NotImplementedError
|
4989
|
+
|
4990
|
+
@abc.abstractmethod
|
4991
|
+
def stop_all(self) -> None:
|
4992
|
+
raise NotImplementedError
|
4993
|
+
|
4994
|
+
@property
|
4995
|
+
@abc.abstractmethod
|
4996
|
+
def name(self) -> str:
|
4997
|
+
raise NotImplementedError
|
4998
|
+
|
4999
|
+
@abc.abstractmethod
|
5000
|
+
def before_remove(self) -> None:
|
5001
|
+
raise NotImplementedError
|
5002
|
+
|
5003
|
+
@abc.abstractmethod
|
5004
|
+
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: # Dispatcher]:
|
5005
|
+
raise NotImplementedError
|
5006
|
+
|
5007
|
+
@abc.abstractmethod
|
5008
|
+
def reopen_logs(self) -> None:
|
5009
|
+
raise NotImplementedError
|
5010
|
+
|
5011
|
+
@abc.abstractmethod
|
5012
|
+
def get_unstopped_processes(self) -> ta.List[Process]:
|
5013
|
+
raise NotImplementedError
|
5014
|
+
|
5015
|
+
@abc.abstractmethod
|
5016
|
+
def after_setuid(self) -> None:
|
5017
|
+
raise NotImplementedError
|
5018
|
+
|
4887
5019
|
|
4888
5020
|
########################################
|
4889
5021
|
# ../context.py
|
4890
5022
|
|
4891
5023
|
|
4892
|
-
|
5024
|
+
ServerEpoch = ta.NewType('ServerEpoch', int)
|
5025
|
+
|
5026
|
+
|
5027
|
+
class ServerContextImpl(ServerContext):
|
4893
5028
|
def __init__(
|
4894
5029
|
self,
|
4895
5030
|
config: ServerConfig,
|
@@ -4903,7 +5038,7 @@ class ServerContext(AbstractServerContext):
|
|
4903
5038
|
self._poller = poller
|
4904
5039
|
self._epoch = epoch
|
4905
5040
|
|
4906
|
-
self._pid_history: ta.Dict[int,
|
5041
|
+
self._pid_history: ta.Dict[int, Process] = {}
|
4907
5042
|
self._state: SupervisorState = SupervisorState.RUNNING
|
4908
5043
|
|
4909
5044
|
if config.user is not None:
|
@@ -4936,7 +5071,7 @@ class ServerContext(AbstractServerContext):
|
|
4936
5071
|
self._state = state
|
4937
5072
|
|
4938
5073
|
@property
|
4939
|
-
def pid_history(self) -> ta.Dict[int,
|
5074
|
+
def pid_history(self) -> ta.Dict[int, Process]:
|
4940
5075
|
return self._pid_history
|
4941
5076
|
|
4942
5077
|
@property
|
@@ -5283,7 +5418,7 @@ def check_execv_args(filename, argv, st) -> None:
|
|
5283
5418
|
class Dispatcher(abc.ABC):
|
5284
5419
|
def __init__(
|
5285
5420
|
self,
|
5286
|
-
process:
|
5421
|
+
process: Process,
|
5287
5422
|
channel: str,
|
5288
5423
|
fd: int,
|
5289
5424
|
*,
|
@@ -5302,7 +5437,7 @@ class Dispatcher(abc.ABC):
|
|
5302
5437
|
return f'<{self.__class__.__name__} at {id(self)} for {self._process} ({self._channel})>'
|
5303
5438
|
|
5304
5439
|
@property
|
5305
|
-
def process(self) ->
|
5440
|
+
def process(self) -> Process:
|
5306
5441
|
return self._process
|
5307
5442
|
|
5308
5443
|
@property
|
@@ -5357,7 +5492,7 @@ class OutputDispatcher(Dispatcher):
|
|
5357
5492
|
|
5358
5493
|
def __init__(
|
5359
5494
|
self,
|
5360
|
-
process:
|
5495
|
+
process: Process,
|
5361
5496
|
event_type: ta.Type[ProcessCommunicationEvent],
|
5362
5497
|
fd: int,
|
5363
5498
|
**kwargs: ta.Any,
|
@@ -5566,7 +5701,7 @@ class OutputDispatcher(Dispatcher):
|
|
5566
5701
|
class InputDispatcher(Dispatcher):
|
5567
5702
|
def __init__(
|
5568
5703
|
self,
|
5569
|
-
process:
|
5704
|
+
process: Process,
|
5570
5705
|
channel: str,
|
5571
5706
|
fd: int,
|
5572
5707
|
**kwargs: ta.Any,
|
@@ -5615,31 +5750,26 @@ class InputDispatcher(Dispatcher):
|
|
5615
5750
|
##
|
5616
5751
|
|
5617
5752
|
|
5618
|
-
|
5619
|
-
class SubprocessFactory:
|
5620
|
-
fn: ta.Callable[[ProcessConfig, AbstractProcessGroup], AbstractSubprocess]
|
5753
|
+
ProcessFactory = ta.NewType('ProcessFactory', Func[Process]) # (config: ProcessConfig, group: ProcessGroup)
|
5621
5754
|
|
5622
|
-
def __call__(self, config: ProcessConfig, group: AbstractProcessGroup) -> AbstractSubprocess:
|
5623
|
-
return self.fn(config, group)
|
5624
5755
|
|
5625
|
-
|
5626
|
-
class ProcessGroup(AbstractProcessGroup):
|
5756
|
+
class ProcessGroupImpl(ProcessGroup):
|
5627
5757
|
def __init__(
|
5628
5758
|
self,
|
5629
5759
|
config: ProcessGroupConfig,
|
5630
5760
|
context: ServerContext,
|
5631
5761
|
*,
|
5632
|
-
|
5762
|
+
process_factory: ProcessFactory,
|
5633
5763
|
):
|
5634
5764
|
super().__init__()
|
5635
5765
|
|
5636
5766
|
self._config = config
|
5637
5767
|
self._context = context
|
5638
|
-
self.
|
5768
|
+
self._process_factory = process_factory
|
5639
5769
|
|
5640
5770
|
self._processes = {}
|
5641
5771
|
for pconfig in self._config.processes or []:
|
5642
|
-
process = self.
|
5772
|
+
process = self._process_factory(pconfig, self)
|
5643
5773
|
self._processes[pconfig.name] = process
|
5644
5774
|
|
5645
5775
|
@property
|
@@ -5651,7 +5781,7 @@ class ProcessGroup(AbstractProcessGroup):
|
|
5651
5781
|
return self._config.name
|
5652
5782
|
|
5653
5783
|
@property
|
5654
|
-
def context(self) ->
|
5784
|
+
def context(self) -> ServerContext:
|
5655
5785
|
return self._context
|
5656
5786
|
|
5657
5787
|
def __repr__(self):
|
@@ -5686,7 +5816,7 @@ class ProcessGroup(AbstractProcessGroup):
|
|
5686
5816
|
# BACKOFF -> FATAL
|
5687
5817
|
proc.give_up()
|
5688
5818
|
|
5689
|
-
def get_unstopped_processes(self) -> ta.List[
|
5819
|
+
def get_unstopped_processes(self) -> ta.List[Process]:
|
5690
5820
|
return [x for x in self._processes.values() if not x.get_state().stopped]
|
5691
5821
|
|
5692
5822
|
def get_dispatchers(self) -> ta.Dict[int, Dispatcher]:
|
@@ -5763,18 +5893,21 @@ class ProcessGroups:
|
|
5763
5893
|
# ../process.py
|
5764
5894
|
|
5765
5895
|
|
5896
|
+
InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
|
5897
|
+
|
5898
|
+
|
5766
5899
|
##
|
5767
5900
|
|
5768
5901
|
|
5769
|
-
class
|
5902
|
+
class ProcessImpl(Process):
|
5770
5903
|
"""A class to manage a subprocess."""
|
5771
5904
|
|
5772
5905
|
def __init__(
|
5773
5906
|
self,
|
5774
5907
|
config: ProcessConfig,
|
5775
|
-
group:
|
5908
|
+
group: ProcessGroup,
|
5776
5909
|
*,
|
5777
|
-
context:
|
5910
|
+
context: ServerContext,
|
5778
5911
|
event_callbacks: EventCallbacks,
|
5779
5912
|
|
5780
5913
|
inherited_fds: ta.Optional[InheritedFds] = None,
|
@@ -5813,7 +5946,7 @@ class Subprocess(AbstractSubprocess):
|
|
5813
5946
|
return self._pid
|
5814
5947
|
|
5815
5948
|
@property
|
5816
|
-
def group(self) ->
|
5949
|
+
def group(self) -> ProcessGroup:
|
5817
5950
|
return self._group
|
5818
5951
|
|
5819
5952
|
@property
|
@@ -5821,7 +5954,7 @@ class Subprocess(AbstractSubprocess):
|
|
5821
5954
|
return self._config
|
5822
5955
|
|
5823
5956
|
@property
|
5824
|
-
def context(self) ->
|
5957
|
+
def context(self) -> ServerContext:
|
5825
5958
|
return self._context
|
5826
5959
|
|
5827
5960
|
@property
|
@@ -6468,7 +6601,7 @@ class SignalHandler:
|
|
6468
6601
|
def __init__(
|
6469
6602
|
self,
|
6470
6603
|
*,
|
6471
|
-
context:
|
6604
|
+
context: ServerContextImpl,
|
6472
6605
|
signal_receiver: SignalReceiver,
|
6473
6606
|
process_groups: ProcessGroups,
|
6474
6607
|
) -> None:
|
@@ -6520,19 +6653,14 @@ class SignalHandler:
|
|
6520
6653
|
##
|
6521
6654
|
|
6522
6655
|
|
6523
|
-
|
6524
|
-
class ProcessGroupFactory:
|
6525
|
-
fn: ta.Callable[[ProcessGroupConfig], ProcessGroup]
|
6526
|
-
|
6527
|
-
def __call__(self, config: ProcessGroupConfig) -> ProcessGroup:
|
6528
|
-
return self.fn(config)
|
6656
|
+
ProcessGroupFactory = ta.NewType('ProcessGroupFactory', Func[ProcessGroup]) # (config: ProcessGroupConfig)
|
6529
6657
|
|
6530
6658
|
|
6531
6659
|
class Supervisor:
|
6532
6660
|
def __init__(
|
6533
6661
|
self,
|
6534
6662
|
*,
|
6535
|
-
context:
|
6663
|
+
context: ServerContextImpl,
|
6536
6664
|
poller: Poller,
|
6537
6665
|
process_groups: ProcessGroups,
|
6538
6666
|
signal_handler: SignalHandler,
|
@@ -6556,7 +6684,7 @@ class Supervisor:
|
|
6556
6684
|
#
|
6557
6685
|
|
6558
6686
|
@property
|
6559
|
-
def context(self) ->
|
6687
|
+
def context(self) -> ServerContextImpl:
|
6560
6688
|
return self._context
|
6561
6689
|
|
6562
6690
|
def get_state(self) -> SupervisorState:
|
@@ -6603,16 +6731,16 @@ class Supervisor:
|
|
6603
6731
|
return True
|
6604
6732
|
|
6605
6733
|
def get_process_map(self) -> ta.Dict[int, Dispatcher]:
|
6606
|
-
process_map = {}
|
6734
|
+
process_map: ta.Dict[int, Dispatcher] = {}
|
6607
6735
|
for group in self._process_groups:
|
6608
6736
|
process_map.update(group.get_dispatchers())
|
6609
6737
|
return process_map
|
6610
6738
|
|
6611
|
-
def shutdown_report(self) -> ta.List[
|
6612
|
-
unstopped: ta.List[
|
6739
|
+
def shutdown_report(self) -> ta.List[Process]:
|
6740
|
+
unstopped: ta.List[Process] = []
|
6613
6741
|
|
6614
6742
|
for group in self._process_groups:
|
6615
|
-
unstopped.extend(group.get_unstopped_processes())
|
6743
|
+
unstopped.extend(group.get_unstopped_processes())
|
6616
6744
|
|
6617
6745
|
if unstopped:
|
6618
6746
|
# throttle 'waiting for x to die' reports
|
@@ -6840,8 +6968,8 @@ def bind_server(
|
|
6840
6968
|
|
6841
6969
|
inj.bind(get_poller_impl(), key=Poller, singleton=True),
|
6842
6970
|
|
6843
|
-
inj.bind(
|
6844
|
-
inj.bind(
|
6971
|
+
inj.bind(ServerContextImpl, singleton=True),
|
6972
|
+
inj.bind(ServerContext, to_key=ServerContextImpl),
|
6845
6973
|
|
6846
6974
|
inj.bind(EventCallbacks, singleton=True),
|
6847
6975
|
|
@@ -6850,21 +6978,10 @@ def bind_server(
|
|
6850
6978
|
inj.bind(SignalHandler, singleton=True),
|
6851
6979
|
inj.bind(ProcessGroups, singleton=True),
|
6852
6980
|
inj.bind(Supervisor, singleton=True),
|
6853
|
-
]
|
6854
6981
|
|
6855
|
-
|
6856
|
-
|
6857
|
-
|
6858
|
-
def inner(group_config: ProcessGroupConfig) -> ProcessGroup:
|
6859
|
-
return injector.inject(functools.partial(ProcessGroup, group_config))
|
6860
|
-
return ProcessGroupFactory(inner)
|
6861
|
-
lst.append(inj.bind(make_process_group_factory))
|
6862
|
-
|
6863
|
-
def make_subprocess_factory(injector: Injector) -> SubprocessFactory:
|
6864
|
-
def inner(process_config: ProcessConfig, group: AbstractProcessGroup) -> AbstractSubprocess:
|
6865
|
-
return injector.inject(functools.partial(Subprocess, process_config, group))
|
6866
|
-
return SubprocessFactory(inner)
|
6867
|
-
lst.append(inj.bind(make_subprocess_factory))
|
6982
|
+
inj.bind_factory(ProcessGroupFactory, ProcessGroupImpl),
|
6983
|
+
inj.bind_factory(ProcessFactory, ProcessImpl),
|
6984
|
+
]
|
6868
6985
|
|
6869
6986
|
#
|
6870
6987
|
|
@@ -6933,7 +7050,7 @@ def main(
|
|
6933
7050
|
inherited_fds=inherited_fds,
|
6934
7051
|
))
|
6935
7052
|
|
6936
|
-
context = injector[
|
7053
|
+
context = injector[ServerContextImpl]
|
6937
7054
|
supervisor = injector[Supervisor]
|
6938
7055
|
|
6939
7056
|
try:
|