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.
@@ -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: InjectorKeyCls
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
- cls: type
2562
+ cls_: type
2523
2563
 
2524
2564
  def __post_init__(self) -> None:
2525
- check_isinstance(self.cls, type)
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.cls)
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
- _INJECTION_SIGNATURE_CACHE: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
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
- def _injection_signature(obj: ta.Any) -> inspect.Signature:
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 _INJECTION_SIGNATURE_CACHE[obj]
2746
+ return _INJECTION_INSPECTION_CACHE[obj]
2684
2747
  except TypeError:
2685
- return inspect.signature(obj)
2748
+ return _do_injection_inspect(obj)
2686
2749
  except KeyError:
2687
2750
  pass
2688
- sig = inspect.signature(obj)
2689
- _INJECTION_SIGNATURE_CACHE[obj] = sig
2690
- return sig
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
- sig = _injection_signature(obj)
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(sig.parameters.values())[skip_args:]:
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(sig)
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
- ) -> InjectorBinding:
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
- sig = _injection_signature(obj)
2829
- key_cls = check_valid_injector_key_cls(sig.return_annotation)
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
- # injector
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
- def inject(self, obj: ta.Any) -> ta.Any:
2936
- kws = self.provide_kwargs(obj)
2937
- return obj(**kws)
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
- ) -> InjectorBinding:
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
- # injector
3088
+ # helpers
3006
3089
 
3007
3090
  @classmethod
3008
- def create_injector(cls, *args: InjectorBindingOrBindings, p: ta.Optional[Injector] = None) -> Injector:
3009
- return _Injector(as_injector_bindings(*args), p)
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 AbstractServerContext(abc.ABC):
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, 'AbstractSubprocess']:
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 AbstractSubprocess(abc.ABC):
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) -> AbstractServerContext:
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]: # dict[int, Dispatcher]
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 AbstractProcessGroup(abc.ABC):
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
- class ServerContext(AbstractServerContext):
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, AbstractSubprocess] = {}
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, AbstractSubprocess]:
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: AbstractSubprocess,
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) -> AbstractSubprocess:
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: AbstractSubprocess,
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: AbstractSubprocess,
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
- @dc.dataclass(frozen=True)
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
- subprocess_factory: SubprocessFactory,
5762
+ process_factory: ProcessFactory,
5633
5763
  ):
5634
5764
  super().__init__()
5635
5765
 
5636
5766
  self._config = config
5637
5767
  self._context = context
5638
- self._subprocess_factory = subprocess_factory
5768
+ self._process_factory = process_factory
5639
5769
 
5640
5770
  self._processes = {}
5641
5771
  for pconfig in self._config.processes or []:
5642
- process = self._subprocess_factory(pconfig, 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) -> AbstractServerContext:
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[AbstractSubprocess]:
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 Subprocess(AbstractSubprocess):
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: AbstractProcessGroup,
5908
+ group: ProcessGroup,
5776
5909
  *,
5777
- context: AbstractServerContext,
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) -> AbstractProcessGroup:
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) -> AbstractServerContext:
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: ServerContext,
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
- @dc.dataclass(frozen=True)
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: ServerContext,
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) -> ServerContext:
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[Subprocess]:
6612
- unstopped: ta.List[Subprocess] = []
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()) # type: ignore
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(ServerContext, singleton=True),
6844
- inj.bind(AbstractServerContext, to_key=ServerContext),
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
- def make_process_group_factory(injector: Injector) -> ProcessGroupFactory:
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[ServerContext]
7053
+ context = injector[ServerContextImpl]
6937
7054
  supervisor = injector[Supervisor]
6938
7055
 
6939
7056
  try: