ominfra 0.0.0.dev142__py3-none-any.whl → 0.0.0.dev144__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/manage/__init__.py +10 -0
- ominfra/manage/__main__.py +4 -0
- ominfra/manage/bootstrap.py +11 -0
- ominfra/manage/bootstrap_.py +18 -0
- ominfra/manage/commands/base.py +133 -1
- ominfra/manage/commands/execution.py +23 -0
- ominfra/manage/commands/inject.py +122 -0
- ominfra/manage/commands/marshal.py +26 -0
- ominfra/manage/config.py +10 -0
- ominfra/manage/deploy/__init__.py +0 -0
- ominfra/manage/deploy/command.py +23 -0
- ominfra/manage/deploy/inject.py +19 -0
- ominfra/manage/inject.py +58 -0
- ominfra/manage/main.py +64 -90
- ominfra/manage/marshal.py +12 -0
- ominfra/manage/remote/__init__.py +0 -0
- ominfra/manage/{protocol.py → remote/channel.py} +9 -2
- ominfra/manage/remote/config.py +12 -0
- ominfra/manage/remote/execution.py +193 -0
- ominfra/manage/remote/inject.py +29 -0
- ominfra/manage/{payload.py → remote/payload.py} +7 -1
- ominfra/manage/{spawning.py → remote/spawning.py} +29 -29
- ominfra/pyremote.py +40 -3
- ominfra/scripts/journald2aws.py +98 -50
- ominfra/scripts/manage.py +2025 -295
- ominfra/scripts/supervisor.py +99 -50
- ominfra/supervisor/inject.py +2 -1
- {ominfra-0.0.0.dev142.dist-info → ominfra-0.0.0.dev144.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev142.dist-info → ominfra-0.0.0.dev144.dist-info}/RECORD +33 -17
- {ominfra-0.0.0.dev142.dist-info → ominfra-0.0.0.dev144.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev142.dist-info → ominfra-0.0.0.dev144.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev142.dist-info → ominfra-0.0.0.dev144.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev142.dist-info → ominfra-0.0.0.dev144.dist-info}/top_level.txt +0 -0
ominfra/scripts/manage.py
CHANGED
@@ -31,6 +31,7 @@ import subprocess
|
|
31
31
|
import sys
|
32
32
|
import threading
|
33
33
|
import time
|
34
|
+
import traceback
|
34
35
|
import types
|
35
36
|
import typing as ta
|
36
37
|
import uuid
|
@@ -48,10 +49,6 @@ if sys.version_info < (3, 8):
|
|
48
49
|
########################################
|
49
50
|
|
50
51
|
|
51
|
-
# commands/base.py
|
52
|
-
CommandT = ta.TypeVar('CommandT', bound='Command')
|
53
|
-
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
54
|
-
|
55
52
|
# ../../omlish/lite/cached.py
|
56
53
|
T = ta.TypeVar('T')
|
57
54
|
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
@@ -59,37 +56,39 @@ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
|
59
56
|
# ../../omlish/lite/check.py
|
60
57
|
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
61
58
|
|
59
|
+
# commands/base.py
|
60
|
+
CommandT = ta.TypeVar('CommandT', bound='Command')
|
61
|
+
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
62
|
+
|
63
|
+
# ../../omlish/lite/inject.py
|
64
|
+
U = ta.TypeVar('U')
|
65
|
+
InjectorKeyCls = ta.Union[type, ta.NewType]
|
66
|
+
InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
|
67
|
+
InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
68
|
+
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
69
|
+
|
62
70
|
# ../../omlish/lite/subprocesses.py
|
63
71
|
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
|
64
72
|
|
65
73
|
|
66
74
|
########################################
|
67
|
-
# ../
|
68
|
-
|
69
|
-
|
70
|
-
##
|
75
|
+
# ../config.py
|
71
76
|
|
72
77
|
|
73
78
|
@dc.dataclass(frozen=True)
|
74
|
-
class
|
75
|
-
|
76
|
-
class Output(abc.ABC): # noqa
|
77
|
-
pass
|
78
|
-
|
79
|
-
|
80
|
-
##
|
79
|
+
class MainConfig:
|
80
|
+
log_level: ta.Optional[str] = 'INFO'
|
81
81
|
|
82
|
-
|
83
|
-
class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
|
84
|
-
@abc.abstractmethod
|
85
|
-
def execute(self, i: CommandT) -> CommandOutputT:
|
86
|
-
raise NotImplementedError
|
82
|
+
debug: bool = False
|
87
83
|
|
88
84
|
|
89
85
|
########################################
|
90
86
|
# ../../pyremote.py
|
91
87
|
"""
|
92
88
|
Basically this: https://mitogen.networkgenomics.com/howitworks.html
|
89
|
+
|
90
|
+
TODO:
|
91
|
+
- log: ta.Optional[logging.Logger] = None + log.debug's
|
93
92
|
"""
|
94
93
|
|
95
94
|
|
@@ -100,6 +99,9 @@ Basically this: https://mitogen.networkgenomics.com/howitworks.html
|
|
100
99
|
class PyremoteBootstrapOptions:
|
101
100
|
debug: bool = False
|
102
101
|
|
102
|
+
DEFAULT_MAIN_NAME_OVERRIDE: ta.ClassVar[str] = '__pyremote__'
|
103
|
+
main_name_override: ta.Optional[str] = DEFAULT_MAIN_NAME_OVERRIDE
|
104
|
+
|
103
105
|
|
104
106
|
##
|
105
107
|
|
@@ -416,6 +418,10 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
|
|
416
418
|
os.dup2(nfd := os.open('/dev/null', os.O_WRONLY), 1)
|
417
419
|
os.close(nfd)
|
418
420
|
|
421
|
+
if (mn := options.main_name_override) is not None:
|
422
|
+
# Inspections like typing.get_type_hints need an entry in sys.modules.
|
423
|
+
sys.modules[mn] = sys.modules['__main__']
|
424
|
+
|
419
425
|
# Write fourth ack
|
420
426
|
output.write(_PYREMOTE_BOOTSTRAP_ACK3)
|
421
427
|
|
@@ -434,14 +440,41 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
|
|
434
440
|
|
435
441
|
|
436
442
|
class PyremoteBootstrapDriver:
|
437
|
-
def __init__(
|
443
|
+
def __init__(
|
444
|
+
self,
|
445
|
+
main_src: ta.Union[str, ta.Sequence[str]],
|
446
|
+
options: PyremoteBootstrapOptions = PyremoteBootstrapOptions(),
|
447
|
+
) -> None:
|
438
448
|
super().__init__()
|
439
449
|
|
440
450
|
self._main_src = main_src
|
441
|
-
self._main_z = zlib.compress(main_src.encode('utf-8'))
|
442
|
-
|
443
451
|
self._options = options
|
452
|
+
|
453
|
+
self._prepared_main_src = self._prepare_main_src(main_src, options)
|
454
|
+
self._main_z = zlib.compress(self._prepared_main_src.encode('utf-8'))
|
455
|
+
|
444
456
|
self._options_json = json.dumps(dc.asdict(options), indent=None, separators=(',', ':')).encode('utf-8') # noqa
|
457
|
+
#
|
458
|
+
|
459
|
+
@classmethod
|
460
|
+
def _prepare_main_src(
|
461
|
+
cls,
|
462
|
+
main_src: ta.Union[str, ta.Sequence[str]],
|
463
|
+
options: PyremoteBootstrapOptions,
|
464
|
+
) -> str:
|
465
|
+
parts: ta.List[str]
|
466
|
+
if isinstance(main_src, str):
|
467
|
+
parts = [main_src]
|
468
|
+
else:
|
469
|
+
parts = list(main_src)
|
470
|
+
|
471
|
+
if (mn := options.main_name_override) is not None:
|
472
|
+
parts.insert(0, f'__name__ = {mn!r}')
|
473
|
+
|
474
|
+
if len(parts) == 1:
|
475
|
+
return parts[0]
|
476
|
+
else:
|
477
|
+
return '\n\n'.join(parts)
|
445
478
|
|
446
479
|
#
|
447
480
|
|
@@ -702,6 +735,99 @@ json_dump_compact: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON
|
|
702
735
|
json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
|
703
736
|
|
704
737
|
|
738
|
+
########################################
|
739
|
+
# ../../../omlish/lite/maybes.py
|
740
|
+
|
741
|
+
|
742
|
+
class Maybe(ta.Generic[T]):
|
743
|
+
@property
|
744
|
+
@abc.abstractmethod
|
745
|
+
def present(self) -> bool:
|
746
|
+
raise NotImplementedError
|
747
|
+
|
748
|
+
@abc.abstractmethod
|
749
|
+
def must(self) -> T:
|
750
|
+
raise NotImplementedError
|
751
|
+
|
752
|
+
@classmethod
|
753
|
+
def just(cls, v: T) -> 'Maybe[T]':
|
754
|
+
return tuple.__new__(_Maybe, (v,)) # noqa
|
755
|
+
|
756
|
+
_empty: ta.ClassVar['Maybe']
|
757
|
+
|
758
|
+
@classmethod
|
759
|
+
def empty(cls) -> 'Maybe[T]':
|
760
|
+
return Maybe._empty
|
761
|
+
|
762
|
+
|
763
|
+
class _Maybe(Maybe[T], tuple):
|
764
|
+
__slots__ = ()
|
765
|
+
|
766
|
+
def __init_subclass__(cls, **kwargs):
|
767
|
+
raise TypeError
|
768
|
+
|
769
|
+
@property
|
770
|
+
def present(self) -> bool:
|
771
|
+
return bool(self)
|
772
|
+
|
773
|
+
def must(self) -> T:
|
774
|
+
if not self:
|
775
|
+
raise ValueError
|
776
|
+
return self[0]
|
777
|
+
|
778
|
+
|
779
|
+
Maybe._empty = tuple.__new__(_Maybe, ()) # noqa
|
780
|
+
|
781
|
+
|
782
|
+
########################################
|
783
|
+
# ../../../omlish/lite/pycharm.py
|
784
|
+
|
785
|
+
|
786
|
+
DEFAULT_PYCHARM_VERSION = '242.23726.102'
|
787
|
+
|
788
|
+
|
789
|
+
@dc.dataclass(frozen=True)
|
790
|
+
class PycharmRemoteDebug:
|
791
|
+
port: int
|
792
|
+
host: ta.Optional[str] = 'localhost'
|
793
|
+
install_version: ta.Optional[str] = DEFAULT_PYCHARM_VERSION
|
794
|
+
|
795
|
+
|
796
|
+
def pycharm_debug_connect(prd: PycharmRemoteDebug) -> None:
|
797
|
+
if prd.install_version is not None:
|
798
|
+
import subprocess
|
799
|
+
import sys
|
800
|
+
subprocess.check_call([
|
801
|
+
sys.executable,
|
802
|
+
'-mpip',
|
803
|
+
'install',
|
804
|
+
f'pydevd-pycharm~={prd.install_version}',
|
805
|
+
])
|
806
|
+
|
807
|
+
pydevd_pycharm = __import__('pydevd_pycharm') # noqa
|
808
|
+
pydevd_pycharm.settrace(
|
809
|
+
prd.host,
|
810
|
+
port=prd.port,
|
811
|
+
stdoutToServer=True,
|
812
|
+
stderrToServer=True,
|
813
|
+
)
|
814
|
+
|
815
|
+
|
816
|
+
def pycharm_debug_preamble(prd: PycharmRemoteDebug) -> str:
|
817
|
+
import inspect
|
818
|
+
import textwrap
|
819
|
+
|
820
|
+
return textwrap.dedent(f"""
|
821
|
+
{inspect.getsource(pycharm_debug_connect)}
|
822
|
+
|
823
|
+
pycharm_debug_connect(PycharmRemoteDebug(
|
824
|
+
{prd.port!r},
|
825
|
+
host={prd.host!r},
|
826
|
+
install_version={prd.install_version!r},
|
827
|
+
))
|
828
|
+
""")
|
829
|
+
|
830
|
+
|
705
831
|
########################################
|
706
832
|
# ../../../omlish/lite/reflect.py
|
707
833
|
|
@@ -758,207 +884,1334 @@ def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
|
758
884
|
|
759
885
|
|
760
886
|
########################################
|
761
|
-
#
|
887
|
+
# ../../../omlish/lite/strings.py
|
762
888
|
|
763
889
|
|
764
|
-
|
765
|
-
def _get_self_src() -> str:
|
766
|
-
return inspect.getsource(sys.modules[__name__])
|
890
|
+
##
|
767
891
|
|
768
892
|
|
769
|
-
def
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
893
|
+
def camel_case(name: str, lower: bool = False) -> str:
|
894
|
+
if not name:
|
895
|
+
return ''
|
896
|
+
s = ''.join(map(str.capitalize, name.split('_'))) # noqa
|
897
|
+
if lower:
|
898
|
+
s = s[0].lower() + s[1:]
|
899
|
+
return s
|
774
900
|
|
775
901
|
|
776
|
-
|
777
|
-
|
778
|
-
return
|
902
|
+
def snake_case(name: str) -> str:
|
903
|
+
uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
|
904
|
+
return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
|
779
905
|
|
780
906
|
|
781
|
-
|
782
|
-
if file is not None:
|
783
|
-
with open(file) as f:
|
784
|
-
return f.read()
|
907
|
+
##
|
785
908
|
|
786
|
-
if _is_self_amalg():
|
787
|
-
return _get_self_src()
|
788
909
|
|
789
|
-
|
790
|
-
return
|
910
|
+
def is_dunder(name: str) -> bool:
|
911
|
+
return (
|
912
|
+
name[:2] == name[-2:] == '__' and
|
913
|
+
name[2:3] != '_' and
|
914
|
+
name[-3:-2] != '_' and
|
915
|
+
len(name) > 4
|
916
|
+
)
|
791
917
|
|
792
918
|
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
919
|
+
def is_sunder(name: str) -> bool:
|
920
|
+
return (
|
921
|
+
name[0] == name[-1] == '_' and
|
922
|
+
name[1:2] != '_' and
|
923
|
+
name[-2:-1] != '_' and
|
924
|
+
len(name) > 2
|
925
|
+
)
|
800
926
|
|
801
927
|
|
802
|
-
|
928
|
+
##
|
929
|
+
|
930
|
+
|
931
|
+
def attr_repr(obj: ta.Any, *attrs: str) -> str:
|
932
|
+
return f'{type(obj).__name__}({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
|
803
933
|
|
804
934
|
|
805
935
|
##
|
806
936
|
|
807
937
|
|
808
|
-
|
938
|
+
FORMAT_NUM_BYTES_SUFFIXES: ta.Sequence[str] = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB']
|
809
939
|
|
810
|
-
def filter(self, record):
|
811
|
-
record.tid = threading.get_native_id()
|
812
|
-
return True
|
813
940
|
|
941
|
+
def format_num_bytes(num_bytes: int) -> str:
|
942
|
+
for i, suffix in enumerate(FORMAT_NUM_BYTES_SUFFIXES):
|
943
|
+
value = num_bytes / 1024 ** i
|
944
|
+
if num_bytes < 1024 ** (i + 1):
|
945
|
+
if value.is_integer():
|
946
|
+
return f'{int(value)}{suffix}'
|
947
|
+
else:
|
948
|
+
return f'{value:.2f}{suffix}'
|
814
949
|
|
815
|
-
|
950
|
+
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
816
951
|
|
817
952
|
|
818
|
-
|
953
|
+
########################################
|
954
|
+
# ../commands/base.py
|
819
955
|
|
820
|
-
KEYS: ta.Mapping[str, bool] = {
|
821
|
-
'name': False,
|
822
|
-
'msg': False,
|
823
|
-
'args': False,
|
824
|
-
'levelname': False,
|
825
|
-
'levelno': False,
|
826
|
-
'pathname': False,
|
827
|
-
'filename': False,
|
828
|
-
'module': False,
|
829
|
-
'exc_info': True,
|
830
|
-
'exc_text': True,
|
831
|
-
'stack_info': True,
|
832
|
-
'lineno': False,
|
833
|
-
'funcName': False,
|
834
|
-
'created': False,
|
835
|
-
'msecs': False,
|
836
|
-
'relativeCreated': False,
|
837
|
-
'thread': False,
|
838
|
-
'threadName': False,
|
839
|
-
'processName': False,
|
840
|
-
'process': False,
|
841
|
-
}
|
842
956
|
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
957
|
+
##
|
958
|
+
|
959
|
+
|
960
|
+
@dc.dataclass(frozen=True)
|
961
|
+
class Command(abc.ABC, ta.Generic[CommandOutputT]):
|
962
|
+
@dc.dataclass(frozen=True)
|
963
|
+
class Output(abc.ABC): # noqa
|
964
|
+
pass
|
965
|
+
|
966
|
+
@ta.final
|
967
|
+
def execute(self, executor: 'CommandExecutor') -> CommandOutputT:
|
968
|
+
return check_isinstance(executor.execute(self), self.Output) # type: ignore[return-value]
|
851
969
|
|
852
970
|
|
853
971
|
##
|
854
972
|
|
855
973
|
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
('levelname', '%(levelname)s'),
|
861
|
-
('name', '%(name)s'),
|
862
|
-
('separator', '::'),
|
863
|
-
('message', '%(message)s'),
|
864
|
-
]
|
974
|
+
@dc.dataclass(frozen=True)
|
975
|
+
class CommandException:
|
976
|
+
name: str
|
977
|
+
repr: str
|
865
978
|
|
979
|
+
traceback: ta.Optional[str] = None
|
866
980
|
|
867
|
-
|
981
|
+
exc: ta.Optional[ta.Any] = None # Exception
|
868
982
|
|
869
|
-
|
870
|
-
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
871
|
-
return ' '.join(v for k, v in parts)
|
983
|
+
cmd: ta.Optional[Command] = None
|
872
984
|
|
873
|
-
|
985
|
+
@classmethod
|
986
|
+
def of(
|
987
|
+
cls,
|
988
|
+
exc: Exception,
|
989
|
+
*,
|
990
|
+
omit_exc_object: bool = False,
|
874
991
|
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
881
|
-
return '%s.%03d' % (t, record.msecs) # noqa
|
992
|
+
cmd: ta.Optional[Command] = None,
|
993
|
+
) -> 'CommandException':
|
994
|
+
return CommandException(
|
995
|
+
name=type(exc).__qualname__,
|
996
|
+
repr=repr(exc),
|
882
997
|
|
998
|
+
traceback=(
|
999
|
+
''.join(traceback.format_tb(exc.__traceback__))
|
1000
|
+
if getattr(exc, '__traceback__', None) is not None else None
|
1001
|
+
),
|
883
1002
|
|
884
|
-
|
1003
|
+
exc=None if omit_exc_object else exc,
|
885
1004
|
|
1005
|
+
cmd=cmd,
|
1006
|
+
)
|
886
1007
|
|
887
|
-
class ProxyLogFilterer(logging.Filterer):
|
888
|
-
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
889
|
-
self._underlying = underlying
|
890
1008
|
|
1009
|
+
class CommandOutputOrException(abc.ABC, ta.Generic[CommandOutputT]):
|
891
1010
|
@property
|
892
|
-
|
893
|
-
|
1011
|
+
@abc.abstractmethod
|
1012
|
+
def output(self) -> ta.Optional[CommandOutputT]:
|
1013
|
+
raise NotImplementedError
|
894
1014
|
|
895
1015
|
@property
|
896
|
-
|
897
|
-
|
1016
|
+
@abc.abstractmethod
|
1017
|
+
def exception(self) -> ta.Optional[CommandException]:
|
1018
|
+
raise NotImplementedError
|
898
1019
|
|
899
|
-
@filters.setter
|
900
|
-
def filters(self, filters):
|
901
|
-
self._underlying.filters = filters
|
902
1020
|
|
903
|
-
|
904
|
-
|
1021
|
+
@dc.dataclass(frozen=True)
|
1022
|
+
class CommandOutputOrExceptionData(CommandOutputOrException):
|
1023
|
+
output: ta.Optional[Command.Output] = None
|
1024
|
+
exception: ta.Optional[CommandException] = None
|
905
1025
|
|
906
|
-
def removeFilter(self, filter): # noqa
|
907
|
-
self._underlying.removeFilter(filter)
|
908
1026
|
|
909
|
-
|
910
|
-
|
1027
|
+
class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
|
1028
|
+
@abc.abstractmethod
|
1029
|
+
def execute(self, cmd: CommandT) -> CommandOutputT:
|
1030
|
+
raise NotImplementedError
|
911
1031
|
|
1032
|
+
def try_execute(
|
1033
|
+
self,
|
1034
|
+
cmd: CommandT,
|
1035
|
+
*,
|
1036
|
+
log: ta.Optional[logging.Logger] = None,
|
1037
|
+
omit_exc_object: bool = False,
|
1038
|
+
) -> CommandOutputOrException[CommandOutputT]:
|
1039
|
+
try:
|
1040
|
+
o = self.execute(cmd)
|
912
1041
|
|
913
|
-
|
914
|
-
|
915
|
-
|
1042
|
+
except Exception as e: # noqa
|
1043
|
+
if log is not None:
|
1044
|
+
log.exception('Exception executing command: %r', type(cmd))
|
916
1045
|
|
917
|
-
|
1046
|
+
return CommandOutputOrExceptionData(exception=CommandException.of(
|
1047
|
+
e,
|
1048
|
+
omit_exc_object=omit_exc_object,
|
1049
|
+
cmd=cmd,
|
1050
|
+
))
|
918
1051
|
|
919
|
-
|
920
|
-
|
921
|
-
return self._underlying
|
1052
|
+
else:
|
1053
|
+
return CommandOutputOrExceptionData(output=o)
|
922
1054
|
|
923
|
-
def get_name(self):
|
924
|
-
return self._underlying.get_name()
|
925
1055
|
|
926
|
-
|
927
|
-
self._underlying.set_name(name)
|
1056
|
+
##
|
928
1057
|
|
929
|
-
@property
|
930
|
-
def name(self):
|
931
|
-
return self._underlying.name
|
932
1058
|
|
933
|
-
|
934
|
-
|
935
|
-
|
1059
|
+
@dc.dataclass(frozen=True)
|
1060
|
+
class CommandRegistration:
|
1061
|
+
command_cls: ta.Type[Command]
|
936
1062
|
|
937
|
-
|
938
|
-
def level(self, level):
|
939
|
-
self._underlying.level = level
|
1063
|
+
name: ta.Optional[str] = None
|
940
1064
|
|
941
1065
|
@property
|
942
|
-
def
|
943
|
-
|
1066
|
+
def name_or_default(self) -> str:
|
1067
|
+
if not (cls_name := self.command_cls.__name__).endswith('Command'):
|
1068
|
+
raise NameError(cls_name)
|
1069
|
+
return snake_case(cls_name[:-len('Command')])
|
944
1070
|
|
945
|
-
@formatter.setter
|
946
|
-
def formatter(self, formatter):
|
947
|
-
self._underlying.formatter = formatter
|
948
1071
|
|
949
|
-
|
950
|
-
self._underlying.createLock()
|
1072
|
+
CommandRegistrations = ta.NewType('CommandRegistrations', ta.Sequence[CommandRegistration])
|
951
1073
|
|
952
|
-
def acquire(self):
|
953
|
-
self._underlying.acquire()
|
954
1074
|
|
955
|
-
|
956
|
-
self._underlying.release()
|
1075
|
+
##
|
957
1076
|
|
958
|
-
def setLevel(self, level):
|
959
|
-
self._underlying.setLevel(level)
|
960
1077
|
|
961
|
-
|
1078
|
+
@dc.dataclass(frozen=True)
|
1079
|
+
class CommandExecutorRegistration:
|
1080
|
+
command_cls: ta.Type[Command]
|
1081
|
+
executor_cls: ta.Type[CommandExecutor]
|
1082
|
+
|
1083
|
+
|
1084
|
+
CommandExecutorRegistrations = ta.NewType('CommandExecutorRegistrations', ta.Sequence[CommandExecutorRegistration])
|
1085
|
+
|
1086
|
+
|
1087
|
+
##
|
1088
|
+
|
1089
|
+
|
1090
|
+
CommandNameMap = ta.NewType('CommandNameMap', ta.Mapping[str, ta.Type[Command]])
|
1091
|
+
|
1092
|
+
|
1093
|
+
def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
1094
|
+
dct: ta.Dict[str, ta.Type[Command]] = {}
|
1095
|
+
cr: CommandRegistration
|
1096
|
+
for cr in crs:
|
1097
|
+
if (name := cr.name_or_default) in dct:
|
1098
|
+
raise NameError(name)
|
1099
|
+
dct[name] = cr.command_cls
|
1100
|
+
return CommandNameMap(dct)
|
1101
|
+
|
1102
|
+
|
1103
|
+
########################################
|
1104
|
+
# ../remote/config.py
|
1105
|
+
|
1106
|
+
|
1107
|
+
@dc.dataclass(frozen=True)
|
1108
|
+
class RemoteConfig:
|
1109
|
+
payload_file: ta.Optional[str] = None
|
1110
|
+
|
1111
|
+
pycharm_remote_debug: ta.Optional[PycharmRemoteDebug] = None
|
1112
|
+
|
1113
|
+
|
1114
|
+
########################################
|
1115
|
+
# ../remote/payload.py
|
1116
|
+
|
1117
|
+
|
1118
|
+
RemoteExecutionPayloadFile = ta.NewType('RemoteExecutionPayloadFile', str)
|
1119
|
+
|
1120
|
+
|
1121
|
+
@cached_nullary
|
1122
|
+
def _get_self_src() -> str:
|
1123
|
+
return inspect.getsource(sys.modules[__name__])
|
1124
|
+
|
1125
|
+
|
1126
|
+
def _is_src_amalg(src: str) -> bool:
|
1127
|
+
for l in src.splitlines(): # noqa
|
1128
|
+
if l.startswith('# @omlish-amalg-output '):
|
1129
|
+
return True
|
1130
|
+
return False
|
1131
|
+
|
1132
|
+
|
1133
|
+
@cached_nullary
|
1134
|
+
def _is_self_amalg() -> bool:
|
1135
|
+
return _is_src_amalg(_get_self_src())
|
1136
|
+
|
1137
|
+
|
1138
|
+
def get_remote_payload_src(
|
1139
|
+
*,
|
1140
|
+
file: ta.Optional[RemoteExecutionPayloadFile],
|
1141
|
+
) -> str:
|
1142
|
+
if file is not None:
|
1143
|
+
with open(file) as f:
|
1144
|
+
return f.read()
|
1145
|
+
|
1146
|
+
if _is_self_amalg():
|
1147
|
+
return _get_self_src()
|
1148
|
+
|
1149
|
+
import importlib.resources
|
1150
|
+
return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
|
1151
|
+
|
1152
|
+
|
1153
|
+
########################################
|
1154
|
+
# ../../../omlish/lite/inject.py
|
1155
|
+
|
1156
|
+
|
1157
|
+
###
|
1158
|
+
# types
|
1159
|
+
|
1160
|
+
|
1161
|
+
@dc.dataclass(frozen=True)
|
1162
|
+
class InjectorKey(ta.Generic[T]):
|
1163
|
+
# Before PEP-560 typing.Generic was a metaclass with a __new__ that takes a 'cls' arg, so instantiating a dataclass
|
1164
|
+
# with kwargs (such as through dc.replace) causes `TypeError: __new__() got multiple values for argument 'cls'`.
|
1165
|
+
# See:
|
1166
|
+
# - https://github.com/python/cpython/commit/d911e40e788fb679723d78b6ea11cabf46caed5a
|
1167
|
+
# - https://gist.github.com/wrmsr/4468b86efe9f373b6b114bfe85b98fd3
|
1168
|
+
cls_: InjectorKeyCls
|
1169
|
+
|
1170
|
+
tag: ta.Any = None
|
1171
|
+
array: bool = False
|
1172
|
+
|
1173
|
+
|
1174
|
+
def is_valid_injector_key_cls(cls: ta.Any) -> bool:
|
1175
|
+
return isinstance(cls, type) or is_new_type(cls)
|
1176
|
+
|
1177
|
+
|
1178
|
+
def check_valid_injector_key_cls(cls: T) -> T:
|
1179
|
+
if not is_valid_injector_key_cls(cls):
|
1180
|
+
raise TypeError(cls)
|
1181
|
+
return cls
|
1182
|
+
|
1183
|
+
|
1184
|
+
##
|
1185
|
+
|
1186
|
+
|
1187
|
+
class InjectorProvider(abc.ABC):
|
1188
|
+
@abc.abstractmethod
|
1189
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1190
|
+
raise NotImplementedError
|
1191
|
+
|
1192
|
+
|
1193
|
+
##
|
1194
|
+
|
1195
|
+
|
1196
|
+
@dc.dataclass(frozen=True)
|
1197
|
+
class InjectorBinding:
|
1198
|
+
key: InjectorKey
|
1199
|
+
provider: InjectorProvider
|
1200
|
+
|
1201
|
+
|
1202
|
+
class InjectorBindings(abc.ABC):
|
1203
|
+
@abc.abstractmethod
|
1204
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
1205
|
+
raise NotImplementedError
|
1206
|
+
|
1207
|
+
##
|
1208
|
+
|
1209
|
+
|
1210
|
+
class Injector(abc.ABC):
|
1211
|
+
@abc.abstractmethod
|
1212
|
+
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
1213
|
+
raise NotImplementedError
|
1214
|
+
|
1215
|
+
@abc.abstractmethod
|
1216
|
+
def provide(self, key: ta.Any) -> ta.Any:
|
1217
|
+
raise NotImplementedError
|
1218
|
+
|
1219
|
+
@abc.abstractmethod
|
1220
|
+
def provide_kwargs(
|
1221
|
+
self,
|
1222
|
+
obj: ta.Any,
|
1223
|
+
*,
|
1224
|
+
skip_args: int = 0,
|
1225
|
+
skip_kwargs: ta.Optional[ta.Iterable[ta.Any]] = None,
|
1226
|
+
) -> ta.Mapping[str, ta.Any]:
|
1227
|
+
raise NotImplementedError
|
1228
|
+
|
1229
|
+
@abc.abstractmethod
|
1230
|
+
def inject(
|
1231
|
+
self,
|
1232
|
+
obj: ta.Any,
|
1233
|
+
*,
|
1234
|
+
args: ta.Optional[ta.Sequence[ta.Any]] = None,
|
1235
|
+
kwargs: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
1236
|
+
) -> ta.Any:
|
1237
|
+
raise NotImplementedError
|
1238
|
+
|
1239
|
+
def __getitem__(
|
1240
|
+
self,
|
1241
|
+
target: ta.Union[InjectorKey[T], ta.Type[T]],
|
1242
|
+
) -> T:
|
1243
|
+
return self.provide(target)
|
1244
|
+
|
1245
|
+
|
1246
|
+
###
|
1247
|
+
# exceptions
|
1248
|
+
|
1249
|
+
|
1250
|
+
class InjectorError(Exception):
|
1251
|
+
pass
|
1252
|
+
|
1253
|
+
|
1254
|
+
@dc.dataclass()
|
1255
|
+
class InjectorKeyError(InjectorError):
|
1256
|
+
key: InjectorKey
|
1257
|
+
|
1258
|
+
source: ta.Any = None
|
1259
|
+
name: ta.Optional[str] = None
|
1260
|
+
|
1261
|
+
|
1262
|
+
class UnboundInjectorKeyError(InjectorKeyError):
|
1263
|
+
pass
|
1264
|
+
|
1265
|
+
|
1266
|
+
class DuplicateInjectorKeyError(InjectorKeyError):
|
1267
|
+
pass
|
1268
|
+
|
1269
|
+
|
1270
|
+
class CyclicDependencyInjectorKeyError(InjectorKeyError):
|
1271
|
+
pass
|
1272
|
+
|
1273
|
+
|
1274
|
+
###
|
1275
|
+
# keys
|
1276
|
+
|
1277
|
+
|
1278
|
+
def as_injector_key(o: ta.Any) -> InjectorKey:
|
1279
|
+
if o is inspect.Parameter.empty:
|
1280
|
+
raise TypeError(o)
|
1281
|
+
if isinstance(o, InjectorKey):
|
1282
|
+
return o
|
1283
|
+
if is_valid_injector_key_cls(o):
|
1284
|
+
return InjectorKey(o)
|
1285
|
+
raise TypeError(o)
|
1286
|
+
|
1287
|
+
|
1288
|
+
###
|
1289
|
+
# providers
|
1290
|
+
|
1291
|
+
|
1292
|
+
@dc.dataclass(frozen=True)
|
1293
|
+
class FnInjectorProvider(InjectorProvider):
|
1294
|
+
fn: ta.Any
|
1295
|
+
|
1296
|
+
def __post_init__(self) -> None:
|
1297
|
+
check_not_isinstance(self.fn, type)
|
1298
|
+
|
1299
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1300
|
+
def pfn(i: Injector) -> ta.Any:
|
1301
|
+
return i.inject(self.fn)
|
1302
|
+
|
1303
|
+
return pfn
|
1304
|
+
|
1305
|
+
|
1306
|
+
@dc.dataclass(frozen=True)
|
1307
|
+
class CtorInjectorProvider(InjectorProvider):
|
1308
|
+
cls_: type
|
1309
|
+
|
1310
|
+
def __post_init__(self) -> None:
|
1311
|
+
check_isinstance(self.cls_, type)
|
1312
|
+
|
1313
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1314
|
+
def pfn(i: Injector) -> ta.Any:
|
1315
|
+
return i.inject(self.cls_)
|
1316
|
+
|
1317
|
+
return pfn
|
1318
|
+
|
1319
|
+
|
1320
|
+
@dc.dataclass(frozen=True)
|
1321
|
+
class ConstInjectorProvider(InjectorProvider):
|
1322
|
+
v: ta.Any
|
1323
|
+
|
1324
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1325
|
+
return lambda _: self.v
|
1326
|
+
|
1327
|
+
|
1328
|
+
@dc.dataclass(frozen=True)
|
1329
|
+
class SingletonInjectorProvider(InjectorProvider):
|
1330
|
+
p: InjectorProvider
|
1331
|
+
|
1332
|
+
def __post_init__(self) -> None:
|
1333
|
+
check_isinstance(self.p, InjectorProvider)
|
1334
|
+
|
1335
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1336
|
+
v = not_set = object()
|
1337
|
+
|
1338
|
+
def pfn(i: Injector) -> ta.Any:
|
1339
|
+
nonlocal v
|
1340
|
+
if v is not_set:
|
1341
|
+
v = ufn(i)
|
1342
|
+
return v
|
1343
|
+
|
1344
|
+
ufn = self.p.provider_fn()
|
1345
|
+
return pfn
|
1346
|
+
|
1347
|
+
|
1348
|
+
@dc.dataclass(frozen=True)
|
1349
|
+
class LinkInjectorProvider(InjectorProvider):
|
1350
|
+
k: InjectorKey
|
1351
|
+
|
1352
|
+
def __post_init__(self) -> None:
|
1353
|
+
check_isinstance(self.k, InjectorKey)
|
1354
|
+
|
1355
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1356
|
+
def pfn(i: Injector) -> ta.Any:
|
1357
|
+
return i.provide(self.k)
|
1358
|
+
|
1359
|
+
return pfn
|
1360
|
+
|
1361
|
+
|
1362
|
+
@dc.dataclass(frozen=True)
|
1363
|
+
class ArrayInjectorProvider(InjectorProvider):
|
1364
|
+
ps: ta.Sequence[InjectorProvider]
|
1365
|
+
|
1366
|
+
def provider_fn(self) -> InjectorProviderFn:
|
1367
|
+
ps = [p.provider_fn() for p in self.ps]
|
1368
|
+
|
1369
|
+
def pfn(i: Injector) -> ta.Any:
|
1370
|
+
rv = []
|
1371
|
+
for ep in ps:
|
1372
|
+
o = ep(i)
|
1373
|
+
rv.append(o)
|
1374
|
+
return rv
|
1375
|
+
|
1376
|
+
return pfn
|
1377
|
+
|
1378
|
+
|
1379
|
+
###
|
1380
|
+
# bindings
|
1381
|
+
|
1382
|
+
|
1383
|
+
@dc.dataclass(frozen=True)
|
1384
|
+
class _InjectorBindings(InjectorBindings):
|
1385
|
+
bs: ta.Optional[ta.Sequence[InjectorBinding]] = None
|
1386
|
+
ps: ta.Optional[ta.Sequence[InjectorBindings]] = None
|
1387
|
+
|
1388
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
1389
|
+
if self.bs is not None:
|
1390
|
+
yield from self.bs
|
1391
|
+
if self.ps is not None:
|
1392
|
+
for p in self.ps:
|
1393
|
+
yield from p.bindings()
|
1394
|
+
|
1395
|
+
|
1396
|
+
def as_injector_bindings(*args: InjectorBindingOrBindings) -> InjectorBindings:
|
1397
|
+
bs: ta.List[InjectorBinding] = []
|
1398
|
+
ps: ta.List[InjectorBindings] = []
|
1399
|
+
|
1400
|
+
for a in args:
|
1401
|
+
if isinstance(a, InjectorBindings):
|
1402
|
+
ps.append(a)
|
1403
|
+
elif isinstance(a, InjectorBinding):
|
1404
|
+
bs.append(a)
|
1405
|
+
else:
|
1406
|
+
raise TypeError(a)
|
1407
|
+
|
1408
|
+
return _InjectorBindings(
|
1409
|
+
bs or None,
|
1410
|
+
ps or None,
|
1411
|
+
)
|
1412
|
+
|
1413
|
+
|
1414
|
+
##
|
1415
|
+
|
1416
|
+
|
1417
|
+
@dc.dataclass(frozen=True)
|
1418
|
+
class OverridesInjectorBindings(InjectorBindings):
|
1419
|
+
p: InjectorBindings
|
1420
|
+
m: ta.Mapping[InjectorKey, InjectorBinding]
|
1421
|
+
|
1422
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
1423
|
+
for b in self.p.bindings():
|
1424
|
+
yield self.m.get(b.key, b)
|
1425
|
+
|
1426
|
+
|
1427
|
+
def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
1428
|
+
m: ta.Dict[InjectorKey, InjectorBinding] = {}
|
1429
|
+
|
1430
|
+
for b in as_injector_bindings(*args).bindings():
|
1431
|
+
if b.key in m:
|
1432
|
+
raise DuplicateInjectorKeyError(b.key)
|
1433
|
+
m[b.key] = b
|
1434
|
+
|
1435
|
+
return OverridesInjectorBindings(p, m)
|
1436
|
+
|
1437
|
+
|
1438
|
+
##
|
1439
|
+
|
1440
|
+
|
1441
|
+
def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey, InjectorProvider]:
|
1442
|
+
pm: ta.Dict[InjectorKey, InjectorProvider] = {}
|
1443
|
+
am: ta.Dict[InjectorKey, ta.List[InjectorProvider]] = {}
|
1444
|
+
|
1445
|
+
for b in bs.bindings():
|
1446
|
+
if b.key.array:
|
1447
|
+
al = am.setdefault(b.key, [])
|
1448
|
+
if isinstance(b.provider, ArrayInjectorProvider):
|
1449
|
+
al.extend(b.provider.ps)
|
1450
|
+
else:
|
1451
|
+
al.append(b.provider)
|
1452
|
+
else:
|
1453
|
+
if b.key in pm:
|
1454
|
+
raise KeyError(b.key)
|
1455
|
+
pm[b.key] = b.provider
|
1456
|
+
|
1457
|
+
if am:
|
1458
|
+
for k, aps in am.items():
|
1459
|
+
pm[k] = ArrayInjectorProvider(aps)
|
1460
|
+
|
1461
|
+
return pm
|
1462
|
+
|
1463
|
+
|
1464
|
+
###
|
1465
|
+
# inspection
|
1466
|
+
|
1467
|
+
|
1468
|
+
class _InjectionInspection(ta.NamedTuple):
|
1469
|
+
signature: inspect.Signature
|
1470
|
+
type_hints: ta.Mapping[str, ta.Any]
|
1471
|
+
args_offset: int
|
1472
|
+
|
1473
|
+
|
1474
|
+
_INJECTION_INSPECTION_CACHE: ta.MutableMapping[ta.Any, _InjectionInspection] = weakref.WeakKeyDictionary()
|
1475
|
+
|
1476
|
+
|
1477
|
+
def _do_injection_inspect(obj: ta.Any) -> _InjectionInspection:
|
1478
|
+
tgt = obj
|
1479
|
+
if isinstance(tgt, type) and tgt.__init__ is not object.__init__: # type: ignore[misc]
|
1480
|
+
# Python 3.8's inspect.signature can't handle subclasses overriding __new__, always generating *args/**kwargs.
|
1481
|
+
# - https://bugs.python.org/issue40897
|
1482
|
+
# - https://github.com/python/cpython/commit/df7c62980d15acd3125dfbd81546dad359f7add7
|
1483
|
+
tgt = tgt.__init__ # type: ignore[misc]
|
1484
|
+
has_generic_base = True
|
1485
|
+
else:
|
1486
|
+
has_generic_base = False
|
1487
|
+
|
1488
|
+
# 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
|
1489
|
+
# eval str annotations *in addition to* getting the signature for parameter information.
|
1490
|
+
uw = tgt
|
1491
|
+
has_partial = False
|
1492
|
+
while True:
|
1493
|
+
if isinstance(uw, functools.partial):
|
1494
|
+
has_partial = True
|
1495
|
+
uw = uw.func
|
1496
|
+
else:
|
1497
|
+
if (uw2 := inspect.unwrap(uw)) is uw:
|
1498
|
+
break
|
1499
|
+
uw = uw2
|
1500
|
+
|
1501
|
+
if has_generic_base and has_partial:
|
1502
|
+
raise InjectorError(
|
1503
|
+
'Injector inspection does not currently support both a typing.Generic base and a functools.partial: '
|
1504
|
+
f'{obj}',
|
1505
|
+
)
|
1506
|
+
|
1507
|
+
return _InjectionInspection(
|
1508
|
+
inspect.signature(tgt),
|
1509
|
+
ta.get_type_hints(uw),
|
1510
|
+
1 if has_generic_base else 0,
|
1511
|
+
)
|
1512
|
+
|
1513
|
+
|
1514
|
+
def _injection_inspect(obj: ta.Any) -> _InjectionInspection:
|
1515
|
+
try:
|
1516
|
+
return _INJECTION_INSPECTION_CACHE[obj]
|
1517
|
+
except TypeError:
|
1518
|
+
return _do_injection_inspect(obj)
|
1519
|
+
except KeyError:
|
1520
|
+
pass
|
1521
|
+
insp = _do_injection_inspect(obj)
|
1522
|
+
_INJECTION_INSPECTION_CACHE[obj] = insp
|
1523
|
+
return insp
|
1524
|
+
|
1525
|
+
|
1526
|
+
class InjectionKwarg(ta.NamedTuple):
|
1527
|
+
name: str
|
1528
|
+
key: InjectorKey
|
1529
|
+
has_default: bool
|
1530
|
+
|
1531
|
+
|
1532
|
+
class InjectionKwargsTarget(ta.NamedTuple):
|
1533
|
+
obj: ta.Any
|
1534
|
+
kwargs: ta.Sequence[InjectionKwarg]
|
1535
|
+
|
1536
|
+
|
1537
|
+
def build_injection_kwargs_target(
|
1538
|
+
obj: ta.Any,
|
1539
|
+
*,
|
1540
|
+
skip_args: int = 0,
|
1541
|
+
skip_kwargs: ta.Optional[ta.Iterable[str]] = None,
|
1542
|
+
raw_optional: bool = False,
|
1543
|
+
) -> InjectionKwargsTarget:
|
1544
|
+
insp = _injection_inspect(obj)
|
1545
|
+
|
1546
|
+
params = list(insp.signature.parameters.values())
|
1547
|
+
|
1548
|
+
skip_names: ta.Set[str] = set()
|
1549
|
+
if skip_kwargs is not None:
|
1550
|
+
skip_names.update(check_not_isinstance(skip_kwargs, str))
|
1551
|
+
|
1552
|
+
seen: ta.Set[InjectorKey] = set()
|
1553
|
+
kws: ta.List[InjectionKwarg] = []
|
1554
|
+
for p in params[insp.args_offset + skip_args:]:
|
1555
|
+
if p.name in skip_names:
|
1556
|
+
continue
|
1557
|
+
|
1558
|
+
if p.annotation is inspect.Signature.empty:
|
1559
|
+
if p.default is not inspect.Parameter.empty:
|
1560
|
+
raise KeyError(f'{obj}, {p.name}')
|
1561
|
+
continue
|
1562
|
+
|
1563
|
+
if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
|
1564
|
+
raise TypeError(insp)
|
1565
|
+
|
1566
|
+
# 3.8 inspect.signature doesn't eval_str but typing.get_type_hints does, so prefer that.
|
1567
|
+
ann = insp.type_hints.get(p.name, p.annotation)
|
1568
|
+
if (
|
1569
|
+
not raw_optional and
|
1570
|
+
is_optional_alias(ann)
|
1571
|
+
):
|
1572
|
+
ann = get_optional_alias_arg(ann)
|
1573
|
+
|
1574
|
+
k = as_injector_key(ann)
|
1575
|
+
|
1576
|
+
if k in seen:
|
1577
|
+
raise DuplicateInjectorKeyError(k)
|
1578
|
+
seen.add(k)
|
1579
|
+
|
1580
|
+
kws.append(InjectionKwarg(
|
1581
|
+
p.name,
|
1582
|
+
k,
|
1583
|
+
p.default is not inspect.Parameter.empty,
|
1584
|
+
))
|
1585
|
+
|
1586
|
+
return InjectionKwargsTarget(
|
1587
|
+
obj,
|
1588
|
+
kws,
|
1589
|
+
)
|
1590
|
+
|
1591
|
+
|
1592
|
+
###
|
1593
|
+
# injector
|
1594
|
+
|
1595
|
+
|
1596
|
+
_INJECTOR_INJECTOR_KEY: InjectorKey[Injector] = InjectorKey(Injector)
|
1597
|
+
|
1598
|
+
|
1599
|
+
@dc.dataclass(frozen=True)
|
1600
|
+
class _InjectorEager:
|
1601
|
+
key: InjectorKey
|
1602
|
+
|
1603
|
+
|
1604
|
+
_INJECTOR_EAGER_ARRAY_KEY: InjectorKey[_InjectorEager] = InjectorKey(_InjectorEager, array=True)
|
1605
|
+
|
1606
|
+
|
1607
|
+
class _Injector(Injector):
|
1608
|
+
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
1609
|
+
super().__init__()
|
1610
|
+
|
1611
|
+
self._bs = check_isinstance(bs, InjectorBindings)
|
1612
|
+
self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
|
1613
|
+
|
1614
|
+
self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
|
1615
|
+
|
1616
|
+
if _INJECTOR_INJECTOR_KEY in self._pfm:
|
1617
|
+
raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
|
1618
|
+
|
1619
|
+
self.__cur_req: ta.Optional[_Injector._Request] = None
|
1620
|
+
|
1621
|
+
if _INJECTOR_EAGER_ARRAY_KEY in self._pfm:
|
1622
|
+
for e in self.provide(_INJECTOR_EAGER_ARRAY_KEY):
|
1623
|
+
self.provide(e.key)
|
1624
|
+
|
1625
|
+
class _Request:
|
1626
|
+
def __init__(self, injector: '_Injector') -> None:
|
1627
|
+
super().__init__()
|
1628
|
+
self._injector = injector
|
1629
|
+
self._provisions: ta.Dict[InjectorKey, Maybe] = {}
|
1630
|
+
self._seen_keys: ta.Set[InjectorKey] = set()
|
1631
|
+
|
1632
|
+
def handle_key(self, key: InjectorKey) -> Maybe[Maybe]:
|
1633
|
+
try:
|
1634
|
+
return Maybe.just(self._provisions[key])
|
1635
|
+
except KeyError:
|
1636
|
+
pass
|
1637
|
+
if key in self._seen_keys:
|
1638
|
+
raise CyclicDependencyInjectorKeyError(key)
|
1639
|
+
self._seen_keys.add(key)
|
1640
|
+
return Maybe.empty()
|
1641
|
+
|
1642
|
+
def handle_provision(self, key: InjectorKey, mv: Maybe) -> Maybe:
|
1643
|
+
check_in(key, self._seen_keys)
|
1644
|
+
check_not_in(key, self._provisions)
|
1645
|
+
self._provisions[key] = mv
|
1646
|
+
return mv
|
1647
|
+
|
1648
|
+
@contextlib.contextmanager
|
1649
|
+
def _current_request(self) -> ta.Generator[_Request, None, None]:
|
1650
|
+
if (cr := self.__cur_req) is not None:
|
1651
|
+
yield cr
|
1652
|
+
return
|
1653
|
+
|
1654
|
+
cr = self._Request(self)
|
1655
|
+
try:
|
1656
|
+
self.__cur_req = cr
|
1657
|
+
yield cr
|
1658
|
+
finally:
|
1659
|
+
self.__cur_req = None
|
1660
|
+
|
1661
|
+
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
1662
|
+
key = as_injector_key(key)
|
1663
|
+
|
1664
|
+
cr: _Injector._Request
|
1665
|
+
with self._current_request() as cr:
|
1666
|
+
if (rv := cr.handle_key(key)).present:
|
1667
|
+
return rv.must()
|
1668
|
+
|
1669
|
+
if key == _INJECTOR_INJECTOR_KEY:
|
1670
|
+
return cr.handle_provision(key, Maybe.just(self))
|
1671
|
+
|
1672
|
+
fn = self._pfm.get(key)
|
1673
|
+
if fn is not None:
|
1674
|
+
return cr.handle_provision(key, Maybe.just(fn(self)))
|
1675
|
+
|
1676
|
+
if self._p is not None:
|
1677
|
+
pv = self._p.try_provide(key)
|
1678
|
+
if pv is not None:
|
1679
|
+
return cr.handle_provision(key, Maybe.empty())
|
1680
|
+
|
1681
|
+
return cr.handle_provision(key, Maybe.empty())
|
1682
|
+
|
1683
|
+
def provide(self, key: ta.Any) -> ta.Any:
|
1684
|
+
v = self.try_provide(key)
|
1685
|
+
if v.present:
|
1686
|
+
return v.must()
|
1687
|
+
raise UnboundInjectorKeyError(key)
|
1688
|
+
|
1689
|
+
def provide_kwargs(
|
1690
|
+
self,
|
1691
|
+
obj: ta.Any,
|
1692
|
+
*,
|
1693
|
+
skip_args: int = 0,
|
1694
|
+
skip_kwargs: ta.Optional[ta.Iterable[ta.Any]] = None,
|
1695
|
+
) -> ta.Mapping[str, ta.Any]:
|
1696
|
+
kt = build_injection_kwargs_target(
|
1697
|
+
obj,
|
1698
|
+
skip_args=skip_args,
|
1699
|
+
skip_kwargs=skip_kwargs,
|
1700
|
+
)
|
1701
|
+
|
1702
|
+
ret: ta.Dict[str, ta.Any] = {}
|
1703
|
+
for kw in kt.kwargs:
|
1704
|
+
if kw.has_default:
|
1705
|
+
if not (mv := self.try_provide(kw.key)).present:
|
1706
|
+
continue
|
1707
|
+
v = mv.must()
|
1708
|
+
else:
|
1709
|
+
v = self.provide(kw.key)
|
1710
|
+
ret[kw.name] = v
|
1711
|
+
return ret
|
1712
|
+
|
1713
|
+
def inject(
|
1714
|
+
self,
|
1715
|
+
obj: ta.Any,
|
1716
|
+
*,
|
1717
|
+
args: ta.Optional[ta.Sequence[ta.Any]] = None,
|
1718
|
+
kwargs: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
1719
|
+
) -> ta.Any:
|
1720
|
+
provided = self.provide_kwargs(
|
1721
|
+
obj,
|
1722
|
+
skip_args=len(args) if args is not None else 0,
|
1723
|
+
skip_kwargs=kwargs if kwargs is not None else None,
|
1724
|
+
)
|
1725
|
+
|
1726
|
+
return obj(
|
1727
|
+
*(args if args is not None else ()),
|
1728
|
+
**(kwargs if kwargs is not None else {}),
|
1729
|
+
**provided,
|
1730
|
+
)
|
1731
|
+
|
1732
|
+
|
1733
|
+
###
|
1734
|
+
# binder
|
1735
|
+
|
1736
|
+
|
1737
|
+
class InjectorBinder:
|
1738
|
+
def __new__(cls, *args, **kwargs): # noqa
|
1739
|
+
raise TypeError
|
1740
|
+
|
1741
|
+
_FN_TYPES: ta.Tuple[type, ...] = (
|
1742
|
+
types.FunctionType,
|
1743
|
+
types.MethodType,
|
1744
|
+
|
1745
|
+
classmethod,
|
1746
|
+
staticmethod,
|
1747
|
+
|
1748
|
+
functools.partial,
|
1749
|
+
functools.partialmethod,
|
1750
|
+
)
|
1751
|
+
|
1752
|
+
@classmethod
|
1753
|
+
def _is_fn(cls, obj: ta.Any) -> bool:
|
1754
|
+
return isinstance(obj, cls._FN_TYPES)
|
1755
|
+
|
1756
|
+
@classmethod
|
1757
|
+
def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
|
1758
|
+
check_isinstance(icls, type)
|
1759
|
+
if icls not in cls._FN_TYPES:
|
1760
|
+
cls._FN_TYPES = (*cls._FN_TYPES, icls)
|
1761
|
+
return icls
|
1762
|
+
|
1763
|
+
_BANNED_BIND_TYPES: ta.Tuple[type, ...] = (
|
1764
|
+
InjectorProvider,
|
1765
|
+
)
|
1766
|
+
|
1767
|
+
@classmethod
|
1768
|
+
def bind(
|
1769
|
+
cls,
|
1770
|
+
obj: ta.Any,
|
1771
|
+
*,
|
1772
|
+
key: ta.Any = None,
|
1773
|
+
tag: ta.Any = None,
|
1774
|
+
array: ta.Optional[bool] = None, # noqa
|
1775
|
+
|
1776
|
+
to_fn: ta.Any = None,
|
1777
|
+
to_ctor: ta.Any = None,
|
1778
|
+
to_const: ta.Any = None,
|
1779
|
+
to_key: ta.Any = None,
|
1780
|
+
|
1781
|
+
singleton: bool = False,
|
1782
|
+
|
1783
|
+
eager: bool = False,
|
1784
|
+
) -> InjectorBindingOrBindings:
|
1785
|
+
if obj is None or obj is inspect.Parameter.empty:
|
1786
|
+
raise TypeError(obj)
|
1787
|
+
if isinstance(obj, cls._BANNED_BIND_TYPES):
|
1788
|
+
raise TypeError(obj)
|
1789
|
+
|
1790
|
+
##
|
1791
|
+
|
1792
|
+
if key is not None:
|
1793
|
+
key = as_injector_key(key)
|
1794
|
+
|
1795
|
+
##
|
1796
|
+
|
1797
|
+
has_to = (
|
1798
|
+
to_fn is not None or
|
1799
|
+
to_ctor is not None or
|
1800
|
+
to_const is not None or
|
1801
|
+
to_key is not None
|
1802
|
+
)
|
1803
|
+
if isinstance(obj, InjectorKey):
|
1804
|
+
if key is None:
|
1805
|
+
key = obj
|
1806
|
+
elif isinstance(obj, type):
|
1807
|
+
if not has_to:
|
1808
|
+
to_ctor = obj
|
1809
|
+
if key is None:
|
1810
|
+
key = InjectorKey(obj)
|
1811
|
+
elif cls._is_fn(obj) and not has_to:
|
1812
|
+
to_fn = obj
|
1813
|
+
if key is None:
|
1814
|
+
insp = _injection_inspect(obj)
|
1815
|
+
key_cls: ta.Any = check_valid_injector_key_cls(check_not_none(insp.type_hints.get('return')))
|
1816
|
+
key = InjectorKey(key_cls)
|
1817
|
+
else:
|
1818
|
+
if to_const is not None:
|
1819
|
+
raise TypeError('Cannot bind instance with to_const')
|
1820
|
+
to_const = obj
|
1821
|
+
if key is None:
|
1822
|
+
key = InjectorKey(type(obj))
|
1823
|
+
del has_to
|
1824
|
+
|
1825
|
+
##
|
1826
|
+
|
1827
|
+
if tag is not None:
|
1828
|
+
if key.tag is not None:
|
1829
|
+
raise TypeError('Tag already set')
|
1830
|
+
key = dc.replace(key, tag=tag)
|
1831
|
+
|
1832
|
+
if array is not None:
|
1833
|
+
key = dc.replace(key, array=array)
|
1834
|
+
|
1835
|
+
##
|
1836
|
+
|
1837
|
+
providers: ta.List[InjectorProvider] = []
|
1838
|
+
if to_fn is not None:
|
1839
|
+
providers.append(FnInjectorProvider(to_fn))
|
1840
|
+
if to_ctor is not None:
|
1841
|
+
providers.append(CtorInjectorProvider(to_ctor))
|
1842
|
+
if to_const is not None:
|
1843
|
+
providers.append(ConstInjectorProvider(to_const))
|
1844
|
+
if to_key is not None:
|
1845
|
+
providers.append(LinkInjectorProvider(as_injector_key(to_key)))
|
1846
|
+
if not providers:
|
1847
|
+
raise TypeError('Must specify provider')
|
1848
|
+
if len(providers) > 1:
|
1849
|
+
raise TypeError('May not specify multiple providers')
|
1850
|
+
provider, = providers
|
1851
|
+
|
1852
|
+
##
|
1853
|
+
|
1854
|
+
if singleton:
|
1855
|
+
provider = SingletonInjectorProvider(provider)
|
1856
|
+
|
1857
|
+
binding = InjectorBinding(key, provider)
|
1858
|
+
|
1859
|
+
##
|
1860
|
+
|
1861
|
+
extras: ta.List[InjectorBinding] = []
|
1862
|
+
|
1863
|
+
if eager:
|
1864
|
+
extras.append(bind_injector_eager_key(key))
|
1865
|
+
|
1866
|
+
##
|
1867
|
+
|
1868
|
+
if extras:
|
1869
|
+
return as_injector_bindings(binding, *extras)
|
1870
|
+
else:
|
1871
|
+
return binding
|
1872
|
+
|
1873
|
+
|
1874
|
+
###
|
1875
|
+
# injection helpers
|
1876
|
+
|
1877
|
+
|
1878
|
+
def make_injector_factory(
|
1879
|
+
fn: ta.Callable[..., T],
|
1880
|
+
cls: U,
|
1881
|
+
ann: ta.Any = None,
|
1882
|
+
) -> ta.Callable[..., U]:
|
1883
|
+
if ann is None:
|
1884
|
+
ann = cls
|
1885
|
+
|
1886
|
+
def outer(injector: Injector) -> ann:
|
1887
|
+
def inner(*args, **kwargs):
|
1888
|
+
return injector.inject(fn, args=args, kwargs=kwargs)
|
1889
|
+
return cls(inner) # type: ignore
|
1890
|
+
|
1891
|
+
return outer
|
1892
|
+
|
1893
|
+
|
1894
|
+
def bind_injector_array(
|
1895
|
+
obj: ta.Any = None,
|
1896
|
+
*,
|
1897
|
+
tag: ta.Any = None,
|
1898
|
+
) -> InjectorBindingOrBindings:
|
1899
|
+
key = as_injector_key(obj)
|
1900
|
+
if tag is not None:
|
1901
|
+
if key.tag is not None:
|
1902
|
+
raise ValueError('Must not specify multiple tags')
|
1903
|
+
key = dc.replace(key, tag=tag)
|
1904
|
+
|
1905
|
+
if key.array:
|
1906
|
+
raise ValueError('Key must not be array')
|
1907
|
+
|
1908
|
+
return InjectorBinding(
|
1909
|
+
dc.replace(key, array=True),
|
1910
|
+
ArrayInjectorProvider([]),
|
1911
|
+
)
|
1912
|
+
|
1913
|
+
|
1914
|
+
def make_injector_array_type(
|
1915
|
+
ele: ta.Union[InjectorKey, InjectorKeyCls],
|
1916
|
+
cls: U,
|
1917
|
+
ann: ta.Any = None,
|
1918
|
+
) -> ta.Callable[..., U]:
|
1919
|
+
if isinstance(ele, InjectorKey):
|
1920
|
+
if not ele.array:
|
1921
|
+
raise InjectorError('Provided key must be array', ele)
|
1922
|
+
key = ele
|
1923
|
+
else:
|
1924
|
+
key = dc.replace(as_injector_key(ele), array=True)
|
1925
|
+
|
1926
|
+
if ann is None:
|
1927
|
+
ann = cls
|
1928
|
+
|
1929
|
+
def inner(injector: Injector) -> ann:
|
1930
|
+
return cls(injector.provide(key)) # type: ignore[operator]
|
1931
|
+
|
1932
|
+
return inner
|
1933
|
+
|
1934
|
+
|
1935
|
+
def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
|
1936
|
+
return InjectorBinding(_INJECTOR_EAGER_ARRAY_KEY, ConstInjectorProvider(_InjectorEager(as_injector_key(key))))
|
1937
|
+
|
1938
|
+
|
1939
|
+
##
|
1940
|
+
|
1941
|
+
|
1942
|
+
class Injection:
|
1943
|
+
def __new__(cls, *args, **kwargs): # noqa
|
1944
|
+
raise TypeError
|
1945
|
+
|
1946
|
+
# keys
|
1947
|
+
|
1948
|
+
@classmethod
|
1949
|
+
def as_key(cls, o: ta.Any) -> InjectorKey:
|
1950
|
+
return as_injector_key(o)
|
1951
|
+
|
1952
|
+
@classmethod
|
1953
|
+
def array(cls, o: ta.Any) -> InjectorKey:
|
1954
|
+
return dc.replace(as_injector_key(o), array=True)
|
1955
|
+
|
1956
|
+
@classmethod
|
1957
|
+
def tag(cls, o: ta.Any, t: ta.Any) -> InjectorKey:
|
1958
|
+
return dc.replace(as_injector_key(o), tag=t)
|
1959
|
+
|
1960
|
+
# bindings
|
1961
|
+
|
1962
|
+
@classmethod
|
1963
|
+
def as_bindings(cls, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
1964
|
+
return as_injector_bindings(*args)
|
1965
|
+
|
1966
|
+
@classmethod
|
1967
|
+
def override(cls, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
1968
|
+
return injector_override(p, *args)
|
1969
|
+
|
1970
|
+
# injector
|
1971
|
+
|
1972
|
+
@classmethod
|
1973
|
+
def create_injector(cls, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
|
1974
|
+
return _Injector(as_injector_bindings(*args), parent)
|
1975
|
+
|
1976
|
+
# binder
|
1977
|
+
|
1978
|
+
@classmethod
|
1979
|
+
def bind(
|
1980
|
+
cls,
|
1981
|
+
obj: ta.Any,
|
1982
|
+
*,
|
1983
|
+
key: ta.Any = None,
|
1984
|
+
tag: ta.Any = None,
|
1985
|
+
array: ta.Optional[bool] = None, # noqa
|
1986
|
+
|
1987
|
+
to_fn: ta.Any = None,
|
1988
|
+
to_ctor: ta.Any = None,
|
1989
|
+
to_const: ta.Any = None,
|
1990
|
+
to_key: ta.Any = None,
|
1991
|
+
|
1992
|
+
singleton: bool = False,
|
1993
|
+
|
1994
|
+
eager: bool = False,
|
1995
|
+
) -> InjectorBindingOrBindings:
|
1996
|
+
return InjectorBinder.bind(
|
1997
|
+
obj,
|
1998
|
+
|
1999
|
+
key=key,
|
2000
|
+
tag=tag,
|
2001
|
+
array=array,
|
2002
|
+
|
2003
|
+
to_fn=to_fn,
|
2004
|
+
to_ctor=to_ctor,
|
2005
|
+
to_const=to_const,
|
2006
|
+
to_key=to_key,
|
2007
|
+
|
2008
|
+
singleton=singleton,
|
2009
|
+
|
2010
|
+
eager=eager,
|
2011
|
+
)
|
2012
|
+
|
2013
|
+
# helpers
|
2014
|
+
|
2015
|
+
@classmethod
|
2016
|
+
def bind_factory(
|
2017
|
+
cls,
|
2018
|
+
fn: ta.Callable[..., T],
|
2019
|
+
cls_: U,
|
2020
|
+
ann: ta.Any = None,
|
2021
|
+
) -> InjectorBindingOrBindings:
|
2022
|
+
return cls.bind(make_injector_factory(fn, cls_, ann))
|
2023
|
+
|
2024
|
+
@classmethod
|
2025
|
+
def bind_array(
|
2026
|
+
cls,
|
2027
|
+
obj: ta.Any = None,
|
2028
|
+
*,
|
2029
|
+
tag: ta.Any = None,
|
2030
|
+
) -> InjectorBindingOrBindings:
|
2031
|
+
return bind_injector_array(obj, tag=tag)
|
2032
|
+
|
2033
|
+
@classmethod
|
2034
|
+
def bind_array_type(
|
2035
|
+
cls,
|
2036
|
+
ele: ta.Union[InjectorKey, InjectorKeyCls],
|
2037
|
+
cls_: U,
|
2038
|
+
ann: ta.Any = None,
|
2039
|
+
) -> InjectorBindingOrBindings:
|
2040
|
+
return cls.bind(make_injector_array_type(ele, cls_, ann))
|
2041
|
+
|
2042
|
+
|
2043
|
+
inj = Injection
|
2044
|
+
|
2045
|
+
|
2046
|
+
########################################
|
2047
|
+
# ../../../omlish/lite/logs.py
|
2048
|
+
"""
|
2049
|
+
TODO:
|
2050
|
+
- translate json keys
|
2051
|
+
- debug
|
2052
|
+
"""
|
2053
|
+
|
2054
|
+
|
2055
|
+
log = logging.getLogger(__name__)
|
2056
|
+
|
2057
|
+
|
2058
|
+
##
|
2059
|
+
|
2060
|
+
|
2061
|
+
class TidLogFilter(logging.Filter):
|
2062
|
+
|
2063
|
+
def filter(self, record):
|
2064
|
+
record.tid = threading.get_native_id()
|
2065
|
+
return True
|
2066
|
+
|
2067
|
+
|
2068
|
+
##
|
2069
|
+
|
2070
|
+
|
2071
|
+
class JsonLogFormatter(logging.Formatter):
|
2072
|
+
|
2073
|
+
KEYS: ta.Mapping[str, bool] = {
|
2074
|
+
'name': False,
|
2075
|
+
'msg': False,
|
2076
|
+
'args': False,
|
2077
|
+
'levelname': False,
|
2078
|
+
'levelno': False,
|
2079
|
+
'pathname': False,
|
2080
|
+
'filename': False,
|
2081
|
+
'module': False,
|
2082
|
+
'exc_info': True,
|
2083
|
+
'exc_text': True,
|
2084
|
+
'stack_info': True,
|
2085
|
+
'lineno': False,
|
2086
|
+
'funcName': False,
|
2087
|
+
'created': False,
|
2088
|
+
'msecs': False,
|
2089
|
+
'relativeCreated': False,
|
2090
|
+
'thread': False,
|
2091
|
+
'threadName': False,
|
2092
|
+
'processName': False,
|
2093
|
+
'process': False,
|
2094
|
+
}
|
2095
|
+
|
2096
|
+
def format(self, record: logging.LogRecord) -> str:
|
2097
|
+
dct = {
|
2098
|
+
k: v
|
2099
|
+
for k, o in self.KEYS.items()
|
2100
|
+
for v in [getattr(record, k)]
|
2101
|
+
if not (o and v is None)
|
2102
|
+
}
|
2103
|
+
return json_dumps_compact(dct)
|
2104
|
+
|
2105
|
+
|
2106
|
+
##
|
2107
|
+
|
2108
|
+
|
2109
|
+
STANDARD_LOG_FORMAT_PARTS = [
|
2110
|
+
('asctime', '%(asctime)-15s'),
|
2111
|
+
('process', 'pid=%(process)-6s'),
|
2112
|
+
('thread', 'tid=%(thread)x'),
|
2113
|
+
('levelname', '%(levelname)s'),
|
2114
|
+
('name', '%(name)s'),
|
2115
|
+
('separator', '::'),
|
2116
|
+
('message', '%(message)s'),
|
2117
|
+
]
|
2118
|
+
|
2119
|
+
|
2120
|
+
class StandardLogFormatter(logging.Formatter):
|
2121
|
+
|
2122
|
+
@staticmethod
|
2123
|
+
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
2124
|
+
return ' '.join(v for k, v in parts)
|
2125
|
+
|
2126
|
+
converter = datetime.datetime.fromtimestamp # type: ignore
|
2127
|
+
|
2128
|
+
def formatTime(self, record, datefmt=None):
|
2129
|
+
ct = self.converter(record.created) # type: ignore
|
2130
|
+
if datefmt:
|
2131
|
+
return ct.strftime(datefmt) # noqa
|
2132
|
+
else:
|
2133
|
+
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
2134
|
+
return '%s.%03d' % (t, record.msecs) # noqa
|
2135
|
+
|
2136
|
+
|
2137
|
+
##
|
2138
|
+
|
2139
|
+
|
2140
|
+
class ProxyLogFilterer(logging.Filterer):
|
2141
|
+
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
2142
|
+
self._underlying = underlying
|
2143
|
+
|
2144
|
+
@property
|
2145
|
+
def underlying(self) -> logging.Filterer:
|
2146
|
+
return self._underlying
|
2147
|
+
|
2148
|
+
@property
|
2149
|
+
def filters(self):
|
2150
|
+
return self._underlying.filters
|
2151
|
+
|
2152
|
+
@filters.setter
|
2153
|
+
def filters(self, filters):
|
2154
|
+
self._underlying.filters = filters
|
2155
|
+
|
2156
|
+
def addFilter(self, filter): # noqa
|
2157
|
+
self._underlying.addFilter(filter)
|
2158
|
+
|
2159
|
+
def removeFilter(self, filter): # noqa
|
2160
|
+
self._underlying.removeFilter(filter)
|
2161
|
+
|
2162
|
+
def filter(self, record):
|
2163
|
+
return self._underlying.filter(record)
|
2164
|
+
|
2165
|
+
|
2166
|
+
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
2167
|
+
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
2168
|
+
ProxyLogFilterer.__init__(self, underlying)
|
2169
|
+
|
2170
|
+
_underlying: logging.Handler
|
2171
|
+
|
2172
|
+
@property
|
2173
|
+
def underlying(self) -> logging.Handler:
|
2174
|
+
return self._underlying
|
2175
|
+
|
2176
|
+
def get_name(self):
|
2177
|
+
return self._underlying.get_name()
|
2178
|
+
|
2179
|
+
def set_name(self, name):
|
2180
|
+
self._underlying.set_name(name)
|
2181
|
+
|
2182
|
+
@property
|
2183
|
+
def name(self):
|
2184
|
+
return self._underlying.name
|
2185
|
+
|
2186
|
+
@property
|
2187
|
+
def level(self):
|
2188
|
+
return self._underlying.level
|
2189
|
+
|
2190
|
+
@level.setter
|
2191
|
+
def level(self, level):
|
2192
|
+
self._underlying.level = level
|
2193
|
+
|
2194
|
+
@property
|
2195
|
+
def formatter(self):
|
2196
|
+
return self._underlying.formatter
|
2197
|
+
|
2198
|
+
@formatter.setter
|
2199
|
+
def formatter(self, formatter):
|
2200
|
+
self._underlying.formatter = formatter
|
2201
|
+
|
2202
|
+
def createLock(self):
|
2203
|
+
self._underlying.createLock()
|
2204
|
+
|
2205
|
+
def acquire(self):
|
2206
|
+
self._underlying.acquire()
|
2207
|
+
|
2208
|
+
def release(self):
|
2209
|
+
self._underlying.release()
|
2210
|
+
|
2211
|
+
def setLevel(self, level):
|
2212
|
+
self._underlying.setLevel(level)
|
2213
|
+
|
2214
|
+
def format(self, record):
|
962
2215
|
return self._underlying.format(record)
|
963
2216
|
|
964
2217
|
def emit(self, record):
|
@@ -1072,21 +2325,26 @@ TODO:
|
|
1072
2325
|
##
|
1073
2326
|
|
1074
2327
|
|
2328
|
+
@dc.dataclass(frozen=True)
|
2329
|
+
class ObjMarshalOptions:
|
2330
|
+
raw_bytes: bool = False
|
2331
|
+
|
2332
|
+
|
1075
2333
|
class ObjMarshaler(abc.ABC):
|
1076
2334
|
@abc.abstractmethod
|
1077
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2335
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1078
2336
|
raise NotImplementedError
|
1079
2337
|
|
1080
2338
|
@abc.abstractmethod
|
1081
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2339
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1082
2340
|
raise NotImplementedError
|
1083
2341
|
|
1084
2342
|
|
1085
2343
|
class NopObjMarshaler(ObjMarshaler):
|
1086
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2344
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1087
2345
|
return o
|
1088
2346
|
|
1089
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2347
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1090
2348
|
return o
|
1091
2349
|
|
1092
2350
|
|
@@ -1094,29 +2352,29 @@ class NopObjMarshaler(ObjMarshaler):
|
|
1094
2352
|
class ProxyObjMarshaler(ObjMarshaler):
|
1095
2353
|
m: ta.Optional[ObjMarshaler] = None
|
1096
2354
|
|
1097
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
1098
|
-
return check_not_none(self.m).marshal(o)
|
2355
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2356
|
+
return check_not_none(self.m).marshal(o, opts)
|
1099
2357
|
|
1100
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
1101
|
-
return check_not_none(self.m).unmarshal(o)
|
2358
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2359
|
+
return check_not_none(self.m).unmarshal(o, opts)
|
1102
2360
|
|
1103
2361
|
|
1104
2362
|
@dc.dataclass(frozen=True)
|
1105
2363
|
class CastObjMarshaler(ObjMarshaler):
|
1106
2364
|
ty: type
|
1107
2365
|
|
1108
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2366
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1109
2367
|
return o
|
1110
2368
|
|
1111
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2369
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1112
2370
|
return self.ty(o)
|
1113
2371
|
|
1114
2372
|
|
1115
2373
|
class DynamicObjMarshaler(ObjMarshaler):
|
1116
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2374
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1117
2375
|
return marshal_obj(o)
|
1118
2376
|
|
1119
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2377
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1120
2378
|
return o
|
1121
2379
|
|
1122
2380
|
|
@@ -1124,21 +2382,36 @@ class DynamicObjMarshaler(ObjMarshaler):
|
|
1124
2382
|
class Base64ObjMarshaler(ObjMarshaler):
|
1125
2383
|
ty: type
|
1126
2384
|
|
1127
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2385
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1128
2386
|
return base64.b64encode(o).decode('ascii')
|
1129
2387
|
|
1130
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2388
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1131
2389
|
return self.ty(base64.b64decode(o))
|
1132
2390
|
|
1133
2391
|
|
2392
|
+
@dc.dataclass(frozen=True)
|
2393
|
+
class BytesSwitchedObjMarshaler(ObjMarshaler):
|
2394
|
+
m: ObjMarshaler
|
2395
|
+
|
2396
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2397
|
+
if opts.raw_bytes:
|
2398
|
+
return o
|
2399
|
+
return self.m.marshal(o, opts)
|
2400
|
+
|
2401
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2402
|
+
if opts.raw_bytes:
|
2403
|
+
return o
|
2404
|
+
return self.m.unmarshal(o, opts)
|
2405
|
+
|
2406
|
+
|
1134
2407
|
@dc.dataclass(frozen=True)
|
1135
2408
|
class EnumObjMarshaler(ObjMarshaler):
|
1136
2409
|
ty: type
|
1137
2410
|
|
1138
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2411
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1139
2412
|
return o.name
|
1140
2413
|
|
1141
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2414
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1142
2415
|
return self.ty.__members__[o] # type: ignore
|
1143
2416
|
|
1144
2417
|
|
@@ -1146,15 +2419,15 @@ class EnumObjMarshaler(ObjMarshaler):
|
|
1146
2419
|
class OptionalObjMarshaler(ObjMarshaler):
|
1147
2420
|
item: ObjMarshaler
|
1148
2421
|
|
1149
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2422
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1150
2423
|
if o is None:
|
1151
2424
|
return None
|
1152
|
-
return self.item.marshal(o)
|
2425
|
+
return self.item.marshal(o, opts)
|
1153
2426
|
|
1154
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2427
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1155
2428
|
if o is None:
|
1156
2429
|
return None
|
1157
|
-
return self.item.unmarshal(o)
|
2430
|
+
return self.item.unmarshal(o, opts)
|
1158
2431
|
|
1159
2432
|
|
1160
2433
|
@dc.dataclass(frozen=True)
|
@@ -1163,11 +2436,11 @@ class MappingObjMarshaler(ObjMarshaler):
|
|
1163
2436
|
km: ObjMarshaler
|
1164
2437
|
vm: ObjMarshaler
|
1165
2438
|
|
1166
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
1167
|
-
return {self.km.marshal(k): self.vm.marshal(v) for k, v in o.items()}
|
2439
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2440
|
+
return {self.km.marshal(k, opts): self.vm.marshal(v, opts) for k, v in o.items()}
|
1168
2441
|
|
1169
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
1170
|
-
return self.ty((self.km.unmarshal(k), self.vm.unmarshal(v)) for k, v in o.items())
|
2442
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2443
|
+
return self.ty((self.km.unmarshal(k, opts), self.vm.unmarshal(v, opts)) for k, v in o.items())
|
1171
2444
|
|
1172
2445
|
|
1173
2446
|
@dc.dataclass(frozen=True)
|
@@ -1175,11 +2448,11 @@ class IterableObjMarshaler(ObjMarshaler):
|
|
1175
2448
|
ty: type
|
1176
2449
|
item: ObjMarshaler
|
1177
2450
|
|
1178
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
1179
|
-
return [self.item.marshal(e) for e in o]
|
2451
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2452
|
+
return [self.item.marshal(e, opts) for e in o]
|
1180
2453
|
|
1181
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
1182
|
-
return self.ty(self.item.unmarshal(e) for e in o)
|
2454
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2455
|
+
return self.ty(self.item.unmarshal(e, opts) for e in o)
|
1183
2456
|
|
1184
2457
|
|
1185
2458
|
@dc.dataclass(frozen=True)
|
@@ -1188,11 +2461,11 @@ class DataclassObjMarshaler(ObjMarshaler):
|
|
1188
2461
|
fs: ta.Mapping[str, ObjMarshaler]
|
1189
2462
|
nonstrict: bool = False
|
1190
2463
|
|
1191
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
1192
|
-
return {k: m.marshal(getattr(o, k)) for k, m in self.fs.items()}
|
2464
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2465
|
+
return {k: m.marshal(getattr(o, k), opts) for k, m in self.fs.items()}
|
1193
2466
|
|
1194
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
1195
|
-
return self.ty(**{k: self.fs[k].unmarshal(v) for k, v in o.items() if not self.nonstrict or k in self.fs})
|
2467
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
2468
|
+
return self.ty(**{k: self.fs[k].unmarshal(v, opts) for k, v in o.items() if not self.nonstrict or k in self.fs})
|
1196
2469
|
|
1197
2470
|
|
1198
2471
|
@dc.dataclass(frozen=True)
|
@@ -1212,50 +2485,50 @@ class PolymorphicObjMarshaler(ObjMarshaler):
|
|
1212
2485
|
{i.tag: i for i in impls},
|
1213
2486
|
)
|
1214
2487
|
|
1215
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2488
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1216
2489
|
impl = self.impls_by_ty[type(o)]
|
1217
|
-
return {impl.tag: impl.m.marshal(o)}
|
2490
|
+
return {impl.tag: impl.m.marshal(o, opts)}
|
1218
2491
|
|
1219
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2492
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1220
2493
|
[(t, v)] = o.items()
|
1221
2494
|
impl = self.impls_by_tag[t]
|
1222
|
-
return impl.m.unmarshal(v)
|
2495
|
+
return impl.m.unmarshal(v, opts)
|
1223
2496
|
|
1224
2497
|
|
1225
2498
|
@dc.dataclass(frozen=True)
|
1226
2499
|
class DatetimeObjMarshaler(ObjMarshaler):
|
1227
2500
|
ty: type
|
1228
2501
|
|
1229
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2502
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1230
2503
|
return o.isoformat()
|
1231
2504
|
|
1232
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2505
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1233
2506
|
return self.ty.fromisoformat(o) # type: ignore
|
1234
2507
|
|
1235
2508
|
|
1236
2509
|
class DecimalObjMarshaler(ObjMarshaler):
|
1237
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2510
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1238
2511
|
return str(check_isinstance(o, decimal.Decimal))
|
1239
2512
|
|
1240
|
-
def unmarshal(self, v: ta.Any) -> ta.Any:
|
2513
|
+
def unmarshal(self, v: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1241
2514
|
return decimal.Decimal(check_isinstance(v, str))
|
1242
2515
|
|
1243
2516
|
|
1244
2517
|
class FractionObjMarshaler(ObjMarshaler):
|
1245
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2518
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1246
2519
|
fr = check_isinstance(o, fractions.Fraction)
|
1247
2520
|
return [fr.numerator, fr.denominator]
|
1248
2521
|
|
1249
|
-
def unmarshal(self, v: ta.Any) -> ta.Any:
|
2522
|
+
def unmarshal(self, v: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1250
2523
|
num, denom = check_isinstance(v, list)
|
1251
2524
|
return fractions.Fraction(num, denom)
|
1252
2525
|
|
1253
2526
|
|
1254
2527
|
class UuidObjMarshaler(ObjMarshaler):
|
1255
|
-
def marshal(self, o: ta.Any) -> ta.Any:
|
2528
|
+
def marshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1256
2529
|
return str(o)
|
1257
2530
|
|
1258
|
-
def unmarshal(self, o: ta.Any) -> ta.Any:
|
2531
|
+
def unmarshal(self, o: ta.Any, opts: ObjMarshalOptions) -> ta.Any:
|
1259
2532
|
return uuid.UUID(o)
|
1260
2533
|
|
1261
2534
|
|
@@ -1265,7 +2538,7 @@ class UuidObjMarshaler(ObjMarshaler):
|
|
1265
2538
|
_DEFAULT_OBJ_MARSHALERS: ta.Dict[ta.Any, ObjMarshaler] = {
|
1266
2539
|
**{t: NopObjMarshaler() for t in (type(None),)},
|
1267
2540
|
**{t: CastObjMarshaler(t) for t in (int, float, str, bool)},
|
1268
|
-
**{t: Base64ObjMarshaler(t) for t in (bytes, bytearray)},
|
2541
|
+
**{t: BytesSwitchedObjMarshaler(Base64ObjMarshaler(t)) for t in (bytes, bytearray)},
|
1269
2542
|
**{t: IterableObjMarshaler(t, DynamicObjMarshaler()) for t in (list, tuple, set, frozenset)},
|
1270
2543
|
**{t: MappingObjMarshaler(t, DynamicObjMarshaler(), DynamicObjMarshaler()) for t in (dict,)},
|
1271
2544
|
|
@@ -1298,12 +2571,16 @@ class ObjMarshalerManager:
|
|
1298
2571
|
def __init__(
|
1299
2572
|
self,
|
1300
2573
|
*,
|
2574
|
+
default_options: ObjMarshalOptions = ObjMarshalOptions(),
|
2575
|
+
|
1301
2576
|
default_obj_marshalers: ta.Dict[ta.Any, ObjMarshaler] = _DEFAULT_OBJ_MARSHALERS, # noqa
|
1302
2577
|
generic_mapping_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES, # noqa
|
1303
2578
|
generic_iterable_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES, # noqa
|
1304
2579
|
) -> None:
|
1305
2580
|
super().__init__()
|
1306
2581
|
|
2582
|
+
self._default_options = default_options
|
2583
|
+
|
1307
2584
|
self._obj_marshalers = dict(default_obj_marshalers)
|
1308
2585
|
self._generic_mapping_types = generic_mapping_types
|
1309
2586
|
self._generic_iterable_types = generic_iterable_types
|
@@ -1412,11 +2689,35 @@ class ObjMarshalerManager:
|
|
1412
2689
|
|
1413
2690
|
#
|
1414
2691
|
|
1415
|
-
def marshal_obj(
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
2692
|
+
def marshal_obj(
|
2693
|
+
self,
|
2694
|
+
o: ta.Any,
|
2695
|
+
ty: ta.Any = None,
|
2696
|
+
opts: ta.Optional[ObjMarshalOptions] = None,
|
2697
|
+
) -> ta.Any:
|
2698
|
+
m = self.get_obj_marshaler(ty if ty is not None else type(o))
|
2699
|
+
return m.marshal(o, opts or self._default_options)
|
2700
|
+
|
2701
|
+
def unmarshal_obj(
|
2702
|
+
self,
|
2703
|
+
o: ta.Any,
|
2704
|
+
ty: ta.Union[ta.Type[T], ta.Any],
|
2705
|
+
opts: ta.Optional[ObjMarshalOptions] = None,
|
2706
|
+
) -> T:
|
2707
|
+
m = self.get_obj_marshaler(ty)
|
2708
|
+
return m.unmarshal(o, opts or self._default_options)
|
2709
|
+
|
2710
|
+
def roundtrip_obj(
|
2711
|
+
self,
|
2712
|
+
o: ta.Any,
|
2713
|
+
ty: ta.Any = None,
|
2714
|
+
opts: ta.Optional[ObjMarshalOptions] = None,
|
2715
|
+
) -> ta.Any:
|
2716
|
+
if ty is None:
|
2717
|
+
ty = type(o)
|
2718
|
+
m: ta.Any = self.marshal_obj(o, ty, opts)
|
2719
|
+
u: ta.Any = self.unmarshal_obj(m, ty, opts)
|
2720
|
+
return u
|
1420
2721
|
|
1421
2722
|
|
1422
2723
|
##
|
@@ -1449,10 +2750,102 @@ def check_runtime_version() -> None:
|
|
1449
2750
|
|
1450
2751
|
|
1451
2752
|
########################################
|
1452
|
-
# ../
|
2753
|
+
# ../bootstrap.py
|
2754
|
+
|
2755
|
+
|
2756
|
+
@dc.dataclass(frozen=True)
|
2757
|
+
class MainBootstrap:
|
2758
|
+
main_config: MainConfig = MainConfig()
|
2759
|
+
|
2760
|
+
remote_config: RemoteConfig = RemoteConfig()
|
2761
|
+
|
2762
|
+
|
2763
|
+
########################################
|
2764
|
+
# ../commands/execution.py
|
2765
|
+
|
2766
|
+
|
2767
|
+
CommandExecutorMap = ta.NewType('CommandExecutorMap', ta.Mapping[ta.Type[Command], CommandExecutor])
|
2768
|
+
|
2769
|
+
|
2770
|
+
class CommandExecutionService(CommandExecutor):
|
2771
|
+
def __init__(
|
2772
|
+
self,
|
2773
|
+
*,
|
2774
|
+
command_executors: CommandExecutorMap,
|
2775
|
+
) -> None:
|
2776
|
+
super().__init__()
|
2777
|
+
|
2778
|
+
self._command_executors = command_executors
|
2779
|
+
|
2780
|
+
def execute(self, cmd: Command) -> Command.Output:
|
2781
|
+
ce: CommandExecutor = self._command_executors[type(cmd)]
|
2782
|
+
return ce.execute(cmd)
|
2783
|
+
|
2784
|
+
|
2785
|
+
########################################
|
2786
|
+
# ../commands/marshal.py
|
2787
|
+
|
2788
|
+
|
2789
|
+
def install_command_marshaling(
|
2790
|
+
cmds: CommandNameMap,
|
2791
|
+
msh: ObjMarshalerManager,
|
2792
|
+
) -> None:
|
2793
|
+
for fn in [
|
2794
|
+
lambda c: c,
|
2795
|
+
lambda c: c.Output,
|
2796
|
+
]:
|
2797
|
+
msh.register_opj_marshaler(
|
2798
|
+
fn(Command),
|
2799
|
+
PolymorphicObjMarshaler.of([
|
2800
|
+
PolymorphicObjMarshaler.Impl(
|
2801
|
+
fn(cmd),
|
2802
|
+
name,
|
2803
|
+
msh.get_obj_marshaler(fn(cmd)),
|
2804
|
+
)
|
2805
|
+
for name, cmd in cmds.items()
|
2806
|
+
]),
|
2807
|
+
)
|
2808
|
+
|
2809
|
+
|
2810
|
+
########################################
|
2811
|
+
# ../deploy/command.py
|
2812
|
+
|
2813
|
+
|
2814
|
+
##
|
2815
|
+
|
2816
|
+
|
2817
|
+
@dc.dataclass(frozen=True)
|
2818
|
+
class DeployCommand(Command['DeployCommand.Output']):
|
2819
|
+
@dc.dataclass(frozen=True)
|
2820
|
+
class Output(Command.Output):
|
2821
|
+
pass
|
2822
|
+
|
2823
|
+
|
2824
|
+
##
|
2825
|
+
|
2826
|
+
|
2827
|
+
class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
|
2828
|
+
def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
|
2829
|
+
return DeployCommand.Output()
|
2830
|
+
|
2831
|
+
|
2832
|
+
########################################
|
2833
|
+
# ../marshal.py
|
2834
|
+
|
2835
|
+
|
2836
|
+
@dc.dataclass(frozen=True)
|
2837
|
+
class ObjMarshalerInstaller:
|
2838
|
+
fn: ta.Callable[[ObjMarshalerManager], None]
|
2839
|
+
|
2840
|
+
|
2841
|
+
ObjMarshalerInstallers = ta.NewType('ObjMarshalerInstallers', ta.Sequence[ObjMarshalerInstaller])
|
2842
|
+
|
2843
|
+
|
2844
|
+
########################################
|
2845
|
+
# ../remote/channel.py
|
1453
2846
|
|
1454
2847
|
|
1455
|
-
class
|
2848
|
+
class RemoteChannel:
|
1456
2849
|
def __init__(
|
1457
2850
|
self,
|
1458
2851
|
input: ta.IO, # noqa
|
@@ -1466,6 +2859,9 @@ class Channel:
|
|
1466
2859
|
self._output = output
|
1467
2860
|
self._msh = msh
|
1468
2861
|
|
2862
|
+
def set_marshaler(self, msh: ObjMarshalerManager) -> None:
|
2863
|
+
self._msh = msh
|
2864
|
+
|
1469
2865
|
def send_obj(self, o: ta.Any, ty: ta.Any = None) -> None:
|
1470
2866
|
j = json_dumps_compact(self._msh.marshal_obj(o, ty))
|
1471
2867
|
d = j.encode('utf-8')
|
@@ -1474,7 +2870,7 @@ class Channel:
|
|
1474
2870
|
self._output.write(d)
|
1475
2871
|
self._output.flush()
|
1476
2872
|
|
1477
|
-
def recv_obj(self, ty: ta.
|
2873
|
+
def recv_obj(self, ty: ta.Type[T]) -> ta.Optional[T]:
|
1478
2874
|
d = self._input.read(4)
|
1479
2875
|
if not d:
|
1480
2876
|
return None
|
@@ -1695,28 +3091,19 @@ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCom
|
|
1695
3091
|
|
1696
3092
|
|
1697
3093
|
########################################
|
1698
|
-
# ../spawning.py
|
3094
|
+
# ../remote/spawning.py
|
1699
3095
|
|
1700
3096
|
|
1701
|
-
class
|
1702
|
-
|
3097
|
+
class RemoteSpawning:
|
3098
|
+
@dc.dataclass(frozen=True)
|
3099
|
+
class Target:
|
3100
|
+
shell: ta.Optional[str] = None
|
3101
|
+
shell_quote: bool = False
|
1703
3102
|
|
1704
|
-
|
1705
|
-
|
1706
|
-
src: str,
|
1707
|
-
*,
|
1708
|
-
shell: ta.Optional[str] = None,
|
1709
|
-
shell_quote: bool = False,
|
1710
|
-
python: str = DEFAULT_PYTHON,
|
1711
|
-
stderr: ta.Optional[SubprocessChannelOption] = None,
|
1712
|
-
) -> None:
|
1713
|
-
super().__init__()
|
3103
|
+
DEFAULT_PYTHON: ta.ClassVar[str] = 'python3'
|
3104
|
+
python: str = DEFAULT_PYTHON
|
1714
3105
|
|
1715
|
-
|
1716
|
-
self._shell = shell
|
1717
|
-
self._shell_quote = shell_quote
|
1718
|
-
self._python = python
|
1719
|
-
self._stderr = stderr
|
3106
|
+
stderr: ta.Optional[str] = None # SubprocessChannelOption
|
1720
3107
|
|
1721
3108
|
#
|
1722
3109
|
|
@@ -1724,20 +3111,24 @@ class PySpawner:
|
|
1724
3111
|
cmd: ta.Sequence[str]
|
1725
3112
|
shell: bool
|
1726
3113
|
|
1727
|
-
def _prepare_cmd(
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
3114
|
+
def _prepare_cmd(
|
3115
|
+
self,
|
3116
|
+
tgt: Target,
|
3117
|
+
src: str,
|
3118
|
+
) -> _PreparedCmd:
|
3119
|
+
if tgt.shell is not None:
|
3120
|
+
sh_src = f'{tgt.python} -c {shlex.quote(src)}'
|
3121
|
+
if tgt.shell_quote:
|
1731
3122
|
sh_src = shlex.quote(sh_src)
|
1732
|
-
sh_cmd = f'{
|
1733
|
-
return
|
3123
|
+
sh_cmd = f'{tgt.shell} {sh_src}'
|
3124
|
+
return RemoteSpawning._PreparedCmd(
|
1734
3125
|
cmd=[sh_cmd],
|
1735
3126
|
shell=True,
|
1736
3127
|
)
|
1737
3128
|
|
1738
3129
|
else:
|
1739
|
-
return
|
1740
|
-
cmd=[
|
3130
|
+
return RemoteSpawning._PreparedCmd(
|
3131
|
+
cmd=[tgt.python, '-c', src],
|
1741
3132
|
shell=False,
|
1742
3133
|
)
|
1743
3134
|
|
@@ -1752,23 +3143,28 @@ class PySpawner:
|
|
1752
3143
|
@contextlib.contextmanager
|
1753
3144
|
def spawn(
|
1754
3145
|
self,
|
3146
|
+
tgt: Target,
|
3147
|
+
src: str,
|
1755
3148
|
*,
|
1756
3149
|
timeout: ta.Optional[float] = None,
|
1757
3150
|
) -> ta.Generator[Spawned, None, None]:
|
1758
|
-
pc = self._prepare_cmd()
|
3151
|
+
pc = self._prepare_cmd(tgt, src)
|
1759
3152
|
|
1760
3153
|
with subprocess.Popen(
|
1761
3154
|
subprocess_maybe_shell_wrap_exec(*pc.cmd),
|
1762
3155
|
shell=pc.shell,
|
1763
3156
|
stdin=subprocess.PIPE,
|
1764
3157
|
stdout=subprocess.PIPE,
|
1765
|
-
stderr=
|
3158
|
+
stderr=(
|
3159
|
+
SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, tgt.stderr)]
|
3160
|
+
if tgt.stderr is not None else None
|
3161
|
+
),
|
1766
3162
|
) as proc:
|
1767
3163
|
stdin = check_not_none(proc.stdin)
|
1768
3164
|
stdout = check_not_none(proc.stdout)
|
1769
3165
|
|
1770
3166
|
try:
|
1771
|
-
yield
|
3167
|
+
yield RemoteSpawning.Spawned(
|
1772
3168
|
stdin=stdin,
|
1773
3169
|
stdout=stdout,
|
1774
3170
|
stderr=proc.stderr,
|
@@ -1784,59 +3180,371 @@ class PySpawner:
|
|
1784
3180
|
|
1785
3181
|
|
1786
3182
|
########################################
|
1787
|
-
#
|
3183
|
+
# ../commands/inject.py
|
1788
3184
|
|
1789
3185
|
|
1790
3186
|
##
|
1791
3187
|
|
1792
3188
|
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
3189
|
+
def bind_command(
|
3190
|
+
command_cls: ta.Type[Command],
|
3191
|
+
executor_cls: ta.Optional[ta.Type[CommandExecutor]],
|
3192
|
+
) -> InjectorBindings:
|
3193
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
3194
|
+
inj.bind(CommandRegistration(command_cls), array=True),
|
3195
|
+
]
|
3196
|
+
|
3197
|
+
if executor_cls is not None:
|
3198
|
+
lst.extend([
|
3199
|
+
inj.bind(executor_cls, singleton=True),
|
3200
|
+
inj.bind(CommandExecutorRegistration(command_cls, executor_cls), array=True),
|
3201
|
+
])
|
3202
|
+
|
3203
|
+
return inj.as_bindings(*lst)
|
1796
3204
|
|
1797
3205
|
|
1798
3206
|
##
|
1799
3207
|
|
1800
3208
|
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1804
|
-
|
3209
|
+
@dc.dataclass(frozen=True)
|
3210
|
+
class _FactoryCommandExecutor(CommandExecutor):
|
3211
|
+
factory: ta.Callable[[], CommandExecutor]
|
3212
|
+
|
3213
|
+
def execute(self, i: Command) -> Command.Output:
|
3214
|
+
return self.factory().execute(i)
|
3215
|
+
|
3216
|
+
|
3217
|
+
##
|
3218
|
+
|
3219
|
+
|
3220
|
+
def bind_commands(
|
3221
|
+
*,
|
3222
|
+
main_config: MainConfig,
|
3223
|
+
) -> InjectorBindings:
|
3224
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
3225
|
+
inj.bind_array(CommandRegistration),
|
3226
|
+
inj.bind_array_type(CommandRegistration, CommandRegistrations),
|
3227
|
+
|
3228
|
+
inj.bind_array(CommandExecutorRegistration),
|
3229
|
+
inj.bind_array_type(CommandExecutorRegistration, CommandExecutorRegistrations),
|
3230
|
+
|
3231
|
+
inj.bind(build_command_name_map, singleton=True),
|
3232
|
+
]
|
3233
|
+
|
3234
|
+
#
|
3235
|
+
|
3236
|
+
def provide_obj_marshaler_installer(cmds: CommandNameMap) -> ObjMarshalerInstaller:
|
3237
|
+
return ObjMarshalerInstaller(functools.partial(install_command_marshaling, cmds))
|
3238
|
+
|
3239
|
+
lst.append(inj.bind(provide_obj_marshaler_installer, array=True))
|
3240
|
+
|
3241
|
+
#
|
3242
|
+
|
3243
|
+
def provide_command_executor_map(
|
3244
|
+
injector: Injector,
|
3245
|
+
crs: CommandExecutorRegistrations,
|
3246
|
+
) -> CommandExecutorMap:
|
3247
|
+
dct: ta.Dict[ta.Type[Command], CommandExecutor] = {}
|
3248
|
+
|
3249
|
+
cr: CommandExecutorRegistration
|
3250
|
+
for cr in crs:
|
3251
|
+
if cr.command_cls in dct:
|
3252
|
+
raise KeyError(cr.command_cls)
|
3253
|
+
|
3254
|
+
factory = functools.partial(injector.provide, cr.executor_cls)
|
3255
|
+
if main_config.debug:
|
3256
|
+
ce = factory()
|
3257
|
+
else:
|
3258
|
+
ce = _FactoryCommandExecutor(factory)
|
3259
|
+
|
3260
|
+
dct[cr.command_cls] = ce
|
3261
|
+
|
3262
|
+
return CommandExecutorMap(dct)
|
3263
|
+
|
3264
|
+
lst.extend([
|
3265
|
+
inj.bind(provide_command_executor_map, singleton=True),
|
3266
|
+
|
3267
|
+
inj.bind(CommandExecutionService, singleton=True, eager=main_config.debug),
|
3268
|
+
inj.bind(CommandExecutor, to_key=CommandExecutionService),
|
3269
|
+
])
|
3270
|
+
|
3271
|
+
#
|
3272
|
+
|
3273
|
+
for command_cls, executor_cls in [
|
3274
|
+
(SubprocessCommand, SubprocessCommandExecutor),
|
1805
3275
|
]:
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
k,
|
1812
|
-
msh.get_obj_marshaler(fn(cty)),
|
1813
|
-
)
|
1814
|
-
for k, cty in _COMMAND_TYPES.items()
|
1815
|
-
]),
|
1816
|
-
)
|
3276
|
+
lst.append(bind_command(command_cls, executor_cls))
|
3277
|
+
|
3278
|
+
#
|
3279
|
+
|
3280
|
+
return inj.as_bindings(*lst)
|
1817
3281
|
|
1818
3282
|
|
1819
|
-
|
3283
|
+
########################################
|
3284
|
+
# ../remote/execution.py
|
1820
3285
|
|
1821
3286
|
|
1822
3287
|
##
|
1823
3288
|
|
1824
3289
|
|
1825
|
-
def
|
3290
|
+
def _remote_execution_main() -> None:
|
1826
3291
|
rt = pyremote_bootstrap_finalize() # noqa
|
1827
|
-
|
3292
|
+
|
3293
|
+
chan = RemoteChannel(
|
3294
|
+
rt.input,
|
3295
|
+
rt.output,
|
3296
|
+
)
|
3297
|
+
|
3298
|
+
bs = check_not_none(chan.recv_obj(MainBootstrap))
|
3299
|
+
|
3300
|
+
if (prd := bs.remote_config.pycharm_remote_debug) is not None:
|
3301
|
+
pycharm_debug_connect(prd)
|
3302
|
+
|
3303
|
+
injector = main_bootstrap(bs)
|
3304
|
+
|
3305
|
+
chan.set_marshaler(injector[ObjMarshalerManager])
|
3306
|
+
|
3307
|
+
ce = injector[CommandExecutor]
|
1828
3308
|
|
1829
3309
|
while True:
|
1830
3310
|
i = chan.recv_obj(Command)
|
1831
3311
|
if i is None:
|
1832
3312
|
break
|
1833
3313
|
|
1834
|
-
|
1835
|
-
|
3314
|
+
r = ce.try_execute(
|
3315
|
+
i,
|
3316
|
+
log=log,
|
3317
|
+
omit_exc_object=True,
|
3318
|
+
)
|
3319
|
+
|
3320
|
+
chan.send_obj(r)
|
3321
|
+
|
3322
|
+
|
3323
|
+
##
|
3324
|
+
|
3325
|
+
|
3326
|
+
@dc.dataclass()
|
3327
|
+
class RemoteCommandError(Exception):
|
3328
|
+
e: CommandException
|
3329
|
+
|
3330
|
+
|
3331
|
+
class RemoteCommandExecutor(CommandExecutor):
|
3332
|
+
def __init__(self, chan: RemoteChannel) -> None:
|
3333
|
+
super().__init__()
|
3334
|
+
|
3335
|
+
self._chan = chan
|
3336
|
+
|
3337
|
+
def _remote_execute(self, cmd: Command) -> CommandOutputOrException:
|
3338
|
+
self._chan.send_obj(cmd, Command)
|
3339
|
+
|
3340
|
+
if (r := self._chan.recv_obj(CommandOutputOrExceptionData)) is None:
|
3341
|
+
raise EOFError
|
3342
|
+
|
3343
|
+
return r
|
3344
|
+
|
3345
|
+
# @ta.override
|
3346
|
+
def execute(self, cmd: Command) -> Command.Output:
|
3347
|
+
r = self._remote_execute(cmd)
|
3348
|
+
if (e := r.exception) is not None:
|
3349
|
+
raise RemoteCommandError(e)
|
3350
|
+
else:
|
3351
|
+
return check_not_none(r.output)
|
3352
|
+
|
3353
|
+
# @ta.override
|
3354
|
+
def try_execute(
|
3355
|
+
self,
|
3356
|
+
cmd: Command,
|
3357
|
+
*,
|
3358
|
+
log: ta.Optional[logging.Logger] = None,
|
3359
|
+
omit_exc_object: bool = False,
|
3360
|
+
) -> CommandOutputOrException:
|
3361
|
+
try:
|
3362
|
+
r = self._remote_execute(cmd)
|
3363
|
+
|
3364
|
+
except Exception as e: # noqa
|
3365
|
+
if log is not None:
|
3366
|
+
log.exception('Exception executing remote command: %r', type(cmd))
|
3367
|
+
|
3368
|
+
return CommandOutputOrExceptionData(exception=CommandException.of(
|
3369
|
+
e,
|
3370
|
+
omit_exc_object=omit_exc_object,
|
3371
|
+
cmd=cmd,
|
3372
|
+
))
|
3373
|
+
|
1836
3374
|
else:
|
1837
|
-
|
3375
|
+
return r
|
3376
|
+
|
3377
|
+
|
3378
|
+
##
|
3379
|
+
|
3380
|
+
|
3381
|
+
class RemoteExecution:
|
3382
|
+
def __init__(
|
3383
|
+
self,
|
3384
|
+
*,
|
3385
|
+
spawning: RemoteSpawning,
|
3386
|
+
msh: ObjMarshalerManager,
|
3387
|
+
payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
|
3388
|
+
) -> None:
|
3389
|
+
super().__init__()
|
3390
|
+
|
3391
|
+
self._spawning = spawning
|
3392
|
+
self._msh = msh
|
3393
|
+
self._payload_file = payload_file
|
3394
|
+
|
3395
|
+
#
|
3396
|
+
|
3397
|
+
@cached_nullary
|
3398
|
+
def _payload_src(self) -> str:
|
3399
|
+
return get_remote_payload_src(file=self._payload_file)
|
3400
|
+
|
3401
|
+
@cached_nullary
|
3402
|
+
def _remote_src(self) -> ta.Sequence[str]:
|
3403
|
+
return [
|
3404
|
+
self._payload_src(),
|
3405
|
+
'_remote_execution_main()',
|
3406
|
+
]
|
3407
|
+
|
3408
|
+
@cached_nullary
|
3409
|
+
def _spawn_src(self) -> str:
|
3410
|
+
return pyremote_build_bootstrap_cmd(__package__ or 'manage')
|
3411
|
+
|
3412
|
+
#
|
3413
|
+
|
3414
|
+
@contextlib.contextmanager
|
3415
|
+
def connect(
|
3416
|
+
self,
|
3417
|
+
tgt: RemoteSpawning.Target,
|
3418
|
+
bs: MainBootstrap,
|
3419
|
+
) -> ta.Generator[RemoteCommandExecutor, None, None]:
|
3420
|
+
spawn_src = self._spawn_src()
|
3421
|
+
remote_src = self._remote_src()
|
3422
|
+
|
3423
|
+
with self._spawning.spawn(
|
3424
|
+
tgt,
|
3425
|
+
spawn_src,
|
3426
|
+
) as proc:
|
3427
|
+
res = PyremoteBootstrapDriver( # noqa
|
3428
|
+
remote_src,
|
3429
|
+
PyremoteBootstrapOptions(
|
3430
|
+
debug=bs.main_config.debug,
|
3431
|
+
),
|
3432
|
+
).run(
|
3433
|
+
proc.stdout,
|
3434
|
+
proc.stdin,
|
3435
|
+
)
|
3436
|
+
|
3437
|
+
chan = RemoteChannel(
|
3438
|
+
proc.stdout,
|
3439
|
+
proc.stdin,
|
3440
|
+
msh=self._msh,
|
3441
|
+
)
|
3442
|
+
|
3443
|
+
chan.send_obj(bs)
|
3444
|
+
|
3445
|
+
yield RemoteCommandExecutor(chan)
|
3446
|
+
|
3447
|
+
|
3448
|
+
########################################
|
3449
|
+
# ../deploy/inject.py
|
1838
3450
|
|
1839
|
-
|
3451
|
+
|
3452
|
+
def bind_deploy(
|
3453
|
+
) -> InjectorBindings:
|
3454
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
3455
|
+
bind_command(DeployCommand, DeployCommandExecutor),
|
3456
|
+
]
|
3457
|
+
|
3458
|
+
return inj.as_bindings(*lst)
|
3459
|
+
|
3460
|
+
|
3461
|
+
########################################
|
3462
|
+
# ../remote/inject.py
|
3463
|
+
|
3464
|
+
|
3465
|
+
def bind_remote(
|
3466
|
+
*,
|
3467
|
+
remote_config: RemoteConfig,
|
3468
|
+
) -> InjectorBindings:
|
3469
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
3470
|
+
inj.bind(remote_config),
|
3471
|
+
|
3472
|
+
inj.bind(RemoteSpawning, singleton=True),
|
3473
|
+
|
3474
|
+
inj.bind(RemoteExecution, singleton=True),
|
3475
|
+
]
|
3476
|
+
|
3477
|
+
if (pf := remote_config.payload_file) is not None:
|
3478
|
+
lst.append(inj.bind(pf, to_key=RemoteExecutionPayloadFile))
|
3479
|
+
|
3480
|
+
return inj.as_bindings(*lst)
|
3481
|
+
|
3482
|
+
|
3483
|
+
########################################
|
3484
|
+
# ../inject.py
|
3485
|
+
|
3486
|
+
|
3487
|
+
##
|
3488
|
+
|
3489
|
+
|
3490
|
+
def bind_main(
|
3491
|
+
*,
|
3492
|
+
main_config: MainConfig,
|
3493
|
+
remote_config: RemoteConfig,
|
3494
|
+
) -> InjectorBindings:
|
3495
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
3496
|
+
inj.bind(main_config),
|
3497
|
+
|
3498
|
+
bind_commands(
|
3499
|
+
main_config=main_config,
|
3500
|
+
),
|
3501
|
+
|
3502
|
+
bind_remote(
|
3503
|
+
remote_config=remote_config,
|
3504
|
+
),
|
3505
|
+
|
3506
|
+
bind_deploy(),
|
3507
|
+
]
|
3508
|
+
|
3509
|
+
#
|
3510
|
+
|
3511
|
+
def build_obj_marshaler_manager(insts: ObjMarshalerInstallers) -> ObjMarshalerManager:
|
3512
|
+
msh = ObjMarshalerManager()
|
3513
|
+
inst: ObjMarshalerInstaller
|
3514
|
+
for inst in insts:
|
3515
|
+
inst.fn(msh)
|
3516
|
+
return msh
|
3517
|
+
|
3518
|
+
lst.extend([
|
3519
|
+
inj.bind(build_obj_marshaler_manager, singleton=True),
|
3520
|
+
|
3521
|
+
inj.bind_array(ObjMarshalerInstaller),
|
3522
|
+
inj.bind_array_type(ObjMarshalerInstaller, ObjMarshalerInstallers),
|
3523
|
+
])
|
3524
|
+
|
3525
|
+
#
|
3526
|
+
|
3527
|
+
return inj.as_bindings(*lst)
|
3528
|
+
|
3529
|
+
|
3530
|
+
########################################
|
3531
|
+
# ../bootstrap_.py
|
3532
|
+
|
3533
|
+
|
3534
|
+
def main_bootstrap(bs: MainBootstrap) -> Injector:
|
3535
|
+
if (log_level := bs.main_config.log_level) is not None:
|
3536
|
+
configure_standard_logging(log_level)
|
3537
|
+
|
3538
|
+
injector = inj.create_injector(bind_main( # noqa
|
3539
|
+
main_config=bs.main_config,
|
3540
|
+
remote_config=bs.remote_config,
|
3541
|
+
))
|
3542
|
+
|
3543
|
+
return injector
|
3544
|
+
|
3545
|
+
|
3546
|
+
########################################
|
3547
|
+
# main.py
|
1840
3548
|
|
1841
3549
|
|
1842
3550
|
##
|
@@ -1853,50 +3561,72 @@ def _main() -> None:
|
|
1853
3561
|
parser.add_argument('-q', '--shell-quote', action='store_true')
|
1854
3562
|
parser.add_argument('--python', default='python3')
|
1855
3563
|
|
3564
|
+
parser.add_argument('--pycharm-debug-port', type=int)
|
3565
|
+
parser.add_argument('--pycharm-debug-host')
|
3566
|
+
parser.add_argument('--pycharm-debug-version')
|
3567
|
+
|
1856
3568
|
parser.add_argument('--debug', action='store_true')
|
1857
3569
|
|
3570
|
+
parser.add_argument('--local', action='store_true')
|
3571
|
+
|
3572
|
+
parser.add_argument('command', nargs='+')
|
3573
|
+
|
1858
3574
|
args = parser.parse_args()
|
1859
3575
|
|
1860
3576
|
#
|
1861
3577
|
|
1862
|
-
|
3578
|
+
bs = MainBootstrap(
|
3579
|
+
main_config=MainConfig(
|
3580
|
+
log_level='DEBUG' if args.debug else 'INFO',
|
1863
3581
|
|
1864
|
-
|
1865
|
-
|
1866
|
-
payload_src,
|
1867
|
-
'_remote_main()',
|
1868
|
-
])
|
3582
|
+
debug=bool(args.debug),
|
3583
|
+
),
|
1869
3584
|
|
1870
|
-
|
3585
|
+
remote_config=RemoteConfig(
|
3586
|
+
payload_file=args._payload_file, # noqa
|
1871
3587
|
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
3588
|
+
pycharm_remote_debug=PycharmRemoteDebug(
|
3589
|
+
port=args.pycharm_debug_port,
|
3590
|
+
host=args.pycharm_debug_host,
|
3591
|
+
install_version=args.pycharm_debug_version,
|
3592
|
+
) if args.pycharm_debug_port is not None else None,
|
3593
|
+
),
|
1877
3594
|
)
|
1878
3595
|
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
PyremoteBootstrapOptions(
|
1883
|
-
debug=args.debug,
|
1884
|
-
),
|
1885
|
-
).run(proc.stdout, proc.stdin)
|
3596
|
+
injector = main_bootstrap(
|
3597
|
+
bs,
|
3598
|
+
)
|
1886
3599
|
|
1887
|
-
|
3600
|
+
#
|
1888
3601
|
|
1889
|
-
|
3602
|
+
cmds: ta.List[Command] = []
|
3603
|
+
for c in args.command:
|
3604
|
+
if c == 'deploy':
|
3605
|
+
cmds.append(DeployCommand())
|
3606
|
+
else:
|
3607
|
+
cmds.append(SubprocessCommand([c]))
|
3608
|
+
|
3609
|
+
#
|
3610
|
+
|
3611
|
+
with contextlib.ExitStack() as es:
|
3612
|
+
ce: CommandExecutor
|
3613
|
+
|
3614
|
+
if args.local:
|
3615
|
+
ce = injector[CommandExecutor]
|
3616
|
+
|
3617
|
+
else:
|
3618
|
+
tgt = RemoteSpawning.Target(
|
3619
|
+
shell=args.shell,
|
3620
|
+
shell_quote=args.shell_quote,
|
3621
|
+
python=args.python,
|
3622
|
+
)
|
1890
3623
|
|
1891
|
-
|
1892
|
-
SubprocessCommand(['python3', '-'], input=b'print(1)\n'),
|
1893
|
-
SubprocessCommand(['uname']),
|
1894
|
-
]:
|
1895
|
-
chan.send_obj(ci, Command)
|
3624
|
+
ce = es.enter_context(injector[RemoteExecution].connect(tgt, bs)) # noqa
|
1896
3625
|
|
1897
|
-
|
3626
|
+
for cmd in cmds:
|
3627
|
+
r = ce.try_execute(cmd)
|
1898
3628
|
|
1899
|
-
print(
|
3629
|
+
print(injector[ObjMarshalerManager].marshal_obj(r, opts=ObjMarshalOptions(raw_bytes=True)))
|
1900
3630
|
|
1901
3631
|
|
1902
3632
|
if __name__ == '__main__':
|