ominfra 0.0.0.dev151__py3-none-any.whl → 0.0.0.dev153__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/clouds/aws/journald2aws/driver.py +1 -1
- ominfra/manage/bootstrap.py +3 -0
- ominfra/manage/bootstrap_.py +1 -0
- ominfra/manage/commands/interp.py +0 -3
- ominfra/manage/commands/subprocess.py +0 -3
- ominfra/manage/deploy/{command.py → commands.py} +0 -3
- ominfra/manage/deploy/inject.py +2 -2
- ominfra/manage/inject.py +8 -1
- ominfra/manage/main.py +7 -7
- ominfra/manage/remote/_main.py +3 -3
- ominfra/manage/remote/config.py +2 -0
- ominfra/manage/remote/connection.py +48 -0
- ominfra/manage/remote/execution.py +90 -12
- ominfra/manage/remote/inject.py +19 -4
- ominfra/manage/system/__init__.py +0 -0
- ominfra/manage/system/commands.py +24 -0
- ominfra/manage/system/config.py +8 -0
- ominfra/manage/system/inject.py +54 -0
- ominfra/manage/system/packages.py +106 -0
- ominfra/manage/system/types.py +5 -0
- ominfra/scripts/journald2aws.py +68 -68
- ominfra/scripts/manage.py +483 -83
- ominfra/scripts/supervisor.py +157 -157
- ominfra/supervisor/main.py +1 -1
- {ominfra-0.0.0.dev151.dist-info → ominfra-0.0.0.dev153.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev151.dist-info → ominfra-0.0.0.dev153.dist-info}/RECORD +30 -24
- {ominfra-0.0.0.dev151.dist-info → ominfra-0.0.0.dev153.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev151.dist-info → ominfra-0.0.0.dev153.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev151.dist-info → ominfra-0.0.0.dev153.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev151.dist-info → ominfra-0.0.0.dev153.dist-info}/top_level.txt +0 -0
ominfra/scripts/manage.py
CHANGED
@@ -68,7 +68,7 @@ VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalTy
|
|
68
68
|
VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # noqa
|
69
69
|
VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool]
|
70
70
|
|
71
|
-
# ../../omlish/
|
71
|
+
# ../../omlish/asyncs/asyncio/timeouts.py
|
72
72
|
AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
|
73
73
|
|
74
74
|
# ../../omlish/lite/cached.py
|
@@ -103,7 +103,10 @@ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
|
103
103
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
104
104
|
|
105
105
|
# ../../omlish/lite/subprocesses.py
|
106
|
-
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
|
106
|
+
SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
|
107
|
+
|
108
|
+
# system/packages.py
|
109
|
+
SystemPackageOrStr = ta.Union['SystemPackage', str]
|
107
110
|
|
108
111
|
|
109
112
|
########################################
|
@@ -524,6 +527,22 @@ class MainConfig:
|
|
524
527
|
debug: bool = False
|
525
528
|
|
526
529
|
|
530
|
+
########################################
|
531
|
+
# ../system/config.py
|
532
|
+
|
533
|
+
|
534
|
+
@dc.dataclass(frozen=True)
|
535
|
+
class SystemConfig:
|
536
|
+
platform: ta.Optional[str] = None
|
537
|
+
|
538
|
+
|
539
|
+
########################################
|
540
|
+
# ../system/types.py
|
541
|
+
|
542
|
+
|
543
|
+
SystemPlatform = ta.NewType('SystemPlatform', str)
|
544
|
+
|
545
|
+
|
527
546
|
########################################
|
528
547
|
# ../../pyremote.py
|
529
548
|
"""
|
@@ -1044,10 +1063,47 @@ class PyremoteBootstrapDriver:
|
|
1044
1063
|
|
1045
1064
|
|
1046
1065
|
########################################
|
1047
|
-
# ../../../omlish/
|
1066
|
+
# ../../../omlish/asyncs/asyncio/channels.py
|
1048
1067
|
|
1049
1068
|
|
1050
|
-
|
1069
|
+
class AsyncioBytesChannelTransport(asyncio.Transport):
|
1070
|
+
def __init__(self, reader: asyncio.StreamReader) -> None:
|
1071
|
+
super().__init__()
|
1072
|
+
|
1073
|
+
self.reader = reader
|
1074
|
+
self.closed: asyncio.Future = asyncio.Future()
|
1075
|
+
|
1076
|
+
# @ta.override
|
1077
|
+
def write(self, data: bytes) -> None:
|
1078
|
+
self.reader.feed_data(data)
|
1079
|
+
|
1080
|
+
# @ta.override
|
1081
|
+
def close(self) -> None:
|
1082
|
+
self.reader.feed_eof()
|
1083
|
+
if not self.closed.done():
|
1084
|
+
self.closed.set_result(True)
|
1085
|
+
|
1086
|
+
# @ta.override
|
1087
|
+
def is_closing(self) -> bool:
|
1088
|
+
return self.closed.done()
|
1089
|
+
|
1090
|
+
|
1091
|
+
def asyncio_create_bytes_channel(
|
1092
|
+
loop: ta.Any = None,
|
1093
|
+
) -> ta.Tuple[asyncio.StreamReader, asyncio.StreamWriter]:
|
1094
|
+
if loop is None:
|
1095
|
+
loop = asyncio.get_running_loop()
|
1096
|
+
|
1097
|
+
reader = asyncio.StreamReader()
|
1098
|
+
protocol = asyncio.StreamReaderProtocol(reader)
|
1099
|
+
transport = AsyncioBytesChannelTransport(reader)
|
1100
|
+
writer = asyncio.StreamWriter(transport, protocol, reader, loop)
|
1101
|
+
|
1102
|
+
return reader, writer
|
1103
|
+
|
1104
|
+
|
1105
|
+
########################################
|
1106
|
+
# ../../../omlish/asyncs/asyncio/streams.py
|
1051
1107
|
|
1052
1108
|
|
1053
1109
|
ASYNCIO_DEFAULT_BUFFER_LIMIT = 2 ** 16
|
@@ -1091,7 +1147,8 @@ async def asyncio_open_stream_writer(
|
|
1091
1147
|
)
|
1092
1148
|
|
1093
1149
|
|
1094
|
-
|
1150
|
+
########################################
|
1151
|
+
# ../../../omlish/asyncs/asyncio/timeouts.py
|
1095
1152
|
|
1096
1153
|
|
1097
1154
|
def asyncio_maybe_timeout(
|
@@ -1611,30 +1668,6 @@ class Checks:
|
|
1611
1668
|
check = Checks()
|
1612
1669
|
|
1613
1670
|
|
1614
|
-
########################################
|
1615
|
-
# ../../../omlish/lite/deathsig.py
|
1616
|
-
|
1617
|
-
|
1618
|
-
LINUX_PR_SET_PDEATHSIG = 1 # Second arg is a signal
|
1619
|
-
LINUX_PR_GET_PDEATHSIG = 2 # Second arg is a ptr to return the signal
|
1620
|
-
|
1621
|
-
|
1622
|
-
def set_process_deathsig(sig: int) -> bool:
|
1623
|
-
if sys.platform == 'linux':
|
1624
|
-
libc = ct.CDLL('libc.so.6')
|
1625
|
-
|
1626
|
-
# int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
1627
|
-
libc.prctl.restype = ct.c_int
|
1628
|
-
libc.prctl.argtypes = [ct.c_int, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong]
|
1629
|
-
|
1630
|
-
libc.prctl(LINUX_PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
|
1631
|
-
|
1632
|
-
return True
|
1633
|
-
|
1634
|
-
else:
|
1635
|
-
return False
|
1636
|
-
|
1637
|
-
|
1638
1671
|
########################################
|
1639
1672
|
# ../../../omlish/lite/json.py
|
1640
1673
|
|
@@ -1880,6 +1913,30 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
1880
1913
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
1881
1914
|
|
1882
1915
|
|
1916
|
+
########################################
|
1917
|
+
# ../../../omlish/os/deathsig.py
|
1918
|
+
|
1919
|
+
|
1920
|
+
LINUX_PR_SET_PDEATHSIG = 1 # Second arg is a signal
|
1921
|
+
LINUX_PR_GET_PDEATHSIG = 2 # Second arg is a ptr to return the signal
|
1922
|
+
|
1923
|
+
|
1924
|
+
def set_process_deathsig(sig: int) -> bool:
|
1925
|
+
if sys.platform == 'linux':
|
1926
|
+
libc = ct.CDLL('libc.so.6')
|
1927
|
+
|
1928
|
+
# int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
1929
|
+
libc.prctl.restype = ct.c_int
|
1930
|
+
libc.prctl.argtypes = [ct.c_int, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong]
|
1931
|
+
|
1932
|
+
libc.prctl(LINUX_PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
|
1933
|
+
|
1934
|
+
return True
|
1935
|
+
|
1936
|
+
else:
|
1937
|
+
return False
|
1938
|
+
|
1939
|
+
|
1883
1940
|
########################################
|
1884
1941
|
# ../../../omdev/packaging/specifiers.py
|
1885
1942
|
# Copyright (c) Donald Stufft and individual contributors.
|
@@ -2572,6 +2629,8 @@ class RemoteConfig:
|
|
2572
2629
|
|
2573
2630
|
heartbeat_interval_s: float = 3.
|
2574
2631
|
|
2632
|
+
use_in_process_remote_executor: bool = False
|
2633
|
+
|
2575
2634
|
|
2576
2635
|
########################################
|
2577
2636
|
# ../remote/payload.py
|
@@ -2618,6 +2677,8 @@ def get_remote_payload_src(
|
|
2618
2677
|
TODO:
|
2619
2678
|
- default command
|
2620
2679
|
- auto match all underscores to hyphens
|
2680
|
+
- pre-run, post-run hooks
|
2681
|
+
- exitstack?
|
2621
2682
|
"""
|
2622
2683
|
|
2623
2684
|
|
@@ -2737,11 +2798,12 @@ class ArgparseCli:
|
|
2737
2798
|
|
2738
2799
|
self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
|
2739
2800
|
|
2801
|
+
#
|
2802
|
+
|
2740
2803
|
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
2741
2804
|
super().__init_subclass__(**kwargs)
|
2742
2805
|
|
2743
2806
|
ns = cls.__dict__
|
2744
|
-
|
2745
2807
|
objs = {}
|
2746
2808
|
mro = cls.__mro__[::-1]
|
2747
2809
|
for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
|
@@ -2754,24 +2816,33 @@ class ArgparseCli:
|
|
2754
2816
|
elif k in objs:
|
2755
2817
|
del [k]
|
2756
2818
|
|
2819
|
+
#
|
2820
|
+
|
2757
2821
|
anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
|
2758
2822
|
**{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
|
2759
2823
|
**ns.get('__annotations__', {}),
|
2760
2824
|
}), globalns=ns.get('__globals__', {}))
|
2761
2825
|
|
2826
|
+
#
|
2827
|
+
|
2762
2828
|
if '_parser' in ns:
|
2763
2829
|
parser = check.isinstance(ns['_parser'], argparse.ArgumentParser)
|
2764
2830
|
else:
|
2765
2831
|
parser = argparse.ArgumentParser()
|
2766
2832
|
setattr(cls, '_parser', parser)
|
2767
2833
|
|
2834
|
+
#
|
2835
|
+
|
2768
2836
|
subparsers = parser.add_subparsers()
|
2837
|
+
|
2769
2838
|
for att, obj in objs.items():
|
2770
2839
|
if isinstance(obj, ArgparseCommand):
|
2771
2840
|
if obj.parent is not None:
|
2772
2841
|
raise NotImplementedError
|
2842
|
+
|
2773
2843
|
for cn in [obj.name, *(obj.aliases or [])]:
|
2774
|
-
|
2844
|
+
subparser = subparsers.add_parser(cn)
|
2845
|
+
|
2775
2846
|
for arg in (obj.args or []):
|
2776
2847
|
if (
|
2777
2848
|
len(arg.args) == 1 and
|
@@ -2779,29 +2850,34 @@ class ArgparseCli:
|
|
2779
2850
|
not (n := check.isinstance(arg.args[0], str)).startswith('-') and
|
2780
2851
|
'metavar' not in arg.kwargs
|
2781
2852
|
):
|
2782
|
-
|
2853
|
+
subparser.add_argument(
|
2783
2854
|
n.replace('-', '_'),
|
2784
2855
|
**arg.kwargs,
|
2785
2856
|
metavar=n,
|
2786
2857
|
)
|
2787
2858
|
else:
|
2788
|
-
|
2789
|
-
|
2859
|
+
subparser.add_argument(*arg.args, **arg.kwargs)
|
2860
|
+
|
2861
|
+
subparser.set_defaults(_cmd=obj)
|
2790
2862
|
|
2791
2863
|
elif isinstance(obj, ArgparseArg):
|
2792
2864
|
if att in anns:
|
2793
|
-
|
2794
|
-
obj.kwargs = {**
|
2865
|
+
ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
|
2866
|
+
obj.kwargs = {**ann_kwargs, **obj.kwargs}
|
2867
|
+
|
2795
2868
|
if not obj.dest:
|
2796
2869
|
if 'dest' in obj.kwargs:
|
2797
2870
|
obj.dest = obj.kwargs['dest']
|
2798
2871
|
else:
|
2799
2872
|
obj.dest = obj.kwargs['dest'] = att # type: ignore
|
2873
|
+
|
2800
2874
|
parser.add_argument(*obj.args, **obj.kwargs)
|
2801
2875
|
|
2802
2876
|
else:
|
2803
2877
|
raise TypeError(obj)
|
2804
2878
|
|
2879
|
+
#
|
2880
|
+
|
2805
2881
|
_parser: ta.ClassVar[argparse.ArgumentParser]
|
2806
2882
|
|
2807
2883
|
@classmethod
|
@@ -2820,10 +2896,12 @@ class ArgparseCli:
|
|
2820
2896
|
def unknown_args(self) -> ta.Sequence[str]:
|
2821
2897
|
return self._unknown_args
|
2822
2898
|
|
2823
|
-
|
2824
|
-
return cmd.__get__(self, type(self))()
|
2899
|
+
#
|
2825
2900
|
|
2826
|
-
def
|
2901
|
+
def _bind_cli_cmd(self, cmd: ArgparseCommand) -> ta.Callable:
|
2902
|
+
return cmd.__get__(self, type(self))
|
2903
|
+
|
2904
|
+
def prepare_cli_run(self) -> ta.Optional[ta.Callable]:
|
2827
2905
|
cmd = getattr(self.args, '_cmd', None)
|
2828
2906
|
|
2829
2907
|
if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
|
@@ -2835,12 +2913,34 @@ class ArgparseCli:
|
|
2835
2913
|
|
2836
2914
|
if cmd is None:
|
2837
2915
|
self.get_parser().print_help()
|
2916
|
+
return None
|
2917
|
+
|
2918
|
+
return self._bind_cli_cmd(cmd)
|
2919
|
+
|
2920
|
+
#
|
2921
|
+
|
2922
|
+
def cli_run(self) -> ta.Optional[int]:
|
2923
|
+
if (fn := self.prepare_cli_run()) is None:
|
2838
2924
|
return 0
|
2839
2925
|
|
2840
|
-
return
|
2926
|
+
return fn()
|
2927
|
+
|
2928
|
+
def cli_run_and_exit(self) -> ta.NoReturn:
|
2929
|
+
sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
|
2930
|
+
|
2931
|
+
def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
|
2932
|
+
if exit:
|
2933
|
+
return self.cli_run_and_exit()
|
2934
|
+
else:
|
2935
|
+
return self.cli_run()
|
2936
|
+
|
2937
|
+
#
|
2938
|
+
|
2939
|
+
async def async_cli_run(self) -> ta.Optional[int]:
|
2940
|
+
if (fn := self.prepare_cli_run()) is None:
|
2941
|
+
return 0
|
2841
2942
|
|
2842
|
-
|
2843
|
-
sys.exit(rc if isinstance(rc := self(), int) else 0)
|
2943
|
+
return await fn()
|
2844
2944
|
|
2845
2945
|
|
2846
2946
|
########################################
|
@@ -4565,6 +4665,8 @@ class MainBootstrap:
|
|
4565
4665
|
|
4566
4666
|
remote_config: RemoteConfig = RemoteConfig()
|
4567
4667
|
|
4668
|
+
system_config: SystemConfig = SystemConfig()
|
4669
|
+
|
4568
4670
|
|
4569
4671
|
########################################
|
4570
4672
|
# ../commands/execution.py
|
@@ -4614,7 +4716,7 @@ def install_command_marshaling(
|
|
4614
4716
|
|
4615
4717
|
|
4616
4718
|
########################################
|
4617
|
-
# ../deploy/
|
4719
|
+
# ../deploy/commands.py
|
4618
4720
|
|
4619
4721
|
|
4620
4722
|
##
|
@@ -4627,9 +4729,6 @@ class DeployCommand(Command['DeployCommand.Output']):
|
|
4627
4729
|
pass
|
4628
4730
|
|
4629
4731
|
|
4630
|
-
##
|
4631
|
-
|
4632
|
-
|
4633
4732
|
class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
|
4634
4733
|
async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
|
4635
4734
|
log.info('Deploying!')
|
@@ -4728,6 +4827,27 @@ class RemoteChannelImpl(RemoteChannel):
|
|
4728
4827
|
return await self._recv_obj(ty)
|
4729
4828
|
|
4730
4829
|
|
4830
|
+
########################################
|
4831
|
+
# ../system/commands.py
|
4832
|
+
|
4833
|
+
|
4834
|
+
##
|
4835
|
+
|
4836
|
+
|
4837
|
+
@dc.dataclass(frozen=True)
|
4838
|
+
class CheckSystemPackageCommand(Command['CheckSystemPackageCommand.Output']):
|
4839
|
+
@dc.dataclass(frozen=True)
|
4840
|
+
class Output(Command.Output):
|
4841
|
+
pass
|
4842
|
+
|
4843
|
+
|
4844
|
+
class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
|
4845
|
+
async def execute(self, cmd: CheckSystemPackageCommand) -> CheckSystemPackageCommand.Output:
|
4846
|
+
log.info('Checking system package!')
|
4847
|
+
|
4848
|
+
return CheckSystemPackageCommand.Output()
|
4849
|
+
|
4850
|
+
|
4731
4851
|
########################################
|
4732
4852
|
# ../../../omlish/lite/subprocesses.py
|
4733
4853
|
|
@@ -4914,6 +5034,10 @@ def subprocess_close(
|
|
4914
5034
|
|
4915
5035
|
########################################
|
4916
5036
|
# ../remote/execution.py
|
5037
|
+
"""
|
5038
|
+
TODO:
|
5039
|
+
- sequence all messages
|
5040
|
+
"""
|
4917
5041
|
|
4918
5042
|
|
4919
5043
|
##
|
@@ -4992,38 +5116,80 @@ class _RemoteLogHandler(logging.Handler):
|
|
4992
5116
|
|
4993
5117
|
|
4994
5118
|
class _RemoteCommandHandler:
|
5119
|
+
DEFAULT_PING_INTERVAL_S: float = 3.
|
5120
|
+
|
4995
5121
|
def __init__(
|
4996
5122
|
self,
|
4997
5123
|
chan: RemoteChannel,
|
4998
5124
|
executor: CommandExecutor,
|
4999
5125
|
*,
|
5000
5126
|
stop: ta.Optional[asyncio.Event] = None,
|
5127
|
+
ping_interval_s: float = DEFAULT_PING_INTERVAL_S,
|
5001
5128
|
) -> None:
|
5002
5129
|
super().__init__()
|
5003
5130
|
|
5004
5131
|
self._chan = chan
|
5005
5132
|
self._executor = executor
|
5006
5133
|
self._stop = stop if stop is not None else asyncio.Event()
|
5134
|
+
self._ping_interval_s = ping_interval_s
|
5007
5135
|
|
5008
5136
|
self._cmds_by_seq: ta.Dict[int, _RemoteCommandHandler._Command] = {}
|
5009
5137
|
|
5138
|
+
self._last_ping_send: float = 0.
|
5139
|
+
self._ping_in_flight: bool = False
|
5140
|
+
self._last_ping_recv: ta.Optional[float] = None
|
5141
|
+
|
5142
|
+
def stop(self) -> None:
|
5143
|
+
self._stop.set()
|
5144
|
+
|
5010
5145
|
@dc.dataclass(frozen=True)
|
5011
5146
|
class _Command:
|
5012
5147
|
req: _RemoteProtocol.CommandRequest
|
5013
5148
|
fut: asyncio.Future
|
5014
5149
|
|
5015
5150
|
async def run(self) -> None:
|
5151
|
+
log.debug('_RemoteCommandHandler loop start: %r', self)
|
5152
|
+
|
5016
5153
|
stop_task = asyncio.create_task(self._stop.wait())
|
5017
5154
|
recv_task: ta.Optional[asyncio.Task] = None
|
5018
5155
|
|
5019
5156
|
while not self._stop.is_set():
|
5020
5157
|
if recv_task is None:
|
5021
|
-
recv_task = asyncio.create_task(_RemoteProtocol.
|
5158
|
+
recv_task = asyncio.create_task(_RemoteProtocol.Message.recv(self._chan))
|
5159
|
+
|
5160
|
+
if not self._ping_in_flight:
|
5161
|
+
if not self._last_ping_recv:
|
5162
|
+
ping_wait_time = 0.
|
5163
|
+
else:
|
5164
|
+
ping_wait_time = self._ping_interval_s - (time.time() - self._last_ping_recv)
|
5165
|
+
else:
|
5166
|
+
ping_wait_time = float('inf')
|
5167
|
+
wait_time = min(self._ping_interval_s, ping_wait_time)
|
5168
|
+
log.debug('_RemoteCommandHandler loop wait: %f', wait_time)
|
5169
|
+
|
5170
|
+
done, pending = await asyncio.wait(
|
5171
|
+
[
|
5172
|
+
stop_task,
|
5173
|
+
recv_task,
|
5174
|
+
],
|
5175
|
+
return_when=asyncio.FIRST_COMPLETED,
|
5176
|
+
timeout=wait_time,
|
5177
|
+
)
|
5022
5178
|
|
5023
|
-
|
5024
|
-
|
5025
|
-
|
5026
|
-
|
5179
|
+
#
|
5180
|
+
|
5181
|
+
if (
|
5182
|
+
(time.time() - self._last_ping_send >= self._ping_interval_s) and
|
5183
|
+
not self._ping_in_flight
|
5184
|
+
):
|
5185
|
+
now = time.time()
|
5186
|
+
self._last_ping_send = now
|
5187
|
+
self._ping_in_flight = True
|
5188
|
+
await _RemoteProtocol.PingRequest(
|
5189
|
+
time=now,
|
5190
|
+
).send(self._chan)
|
5191
|
+
|
5192
|
+
#
|
5027
5193
|
|
5028
5194
|
if recv_task in done:
|
5029
5195
|
msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
|
@@ -5037,6 +5203,20 @@ class _RemoteCommandHandler:
|
|
5037
5203
|
|
5038
5204
|
await self._handle_message(msg)
|
5039
5205
|
|
5206
|
+
log.debug('_RemoteCommandHandler loop stopping: %r', self)
|
5207
|
+
|
5208
|
+
for task in [
|
5209
|
+
stop_task,
|
5210
|
+
recv_task,
|
5211
|
+
]:
|
5212
|
+
if task is not None and not task.done():
|
5213
|
+
task.cancel()
|
5214
|
+
|
5215
|
+
for cmd in self._cmds_by_seq.values():
|
5216
|
+
cmd.fut.cancel()
|
5217
|
+
|
5218
|
+
log.debug('_RemoteCommandHandler loop exited: %r', self)
|
5219
|
+
|
5040
5220
|
async def _handle_message(self, msg: _RemoteProtocol.Message) -> None:
|
5041
5221
|
if isinstance(msg, _RemoteProtocol.PingRequest):
|
5042
5222
|
log.debug('Ping: %r', msg)
|
@@ -5044,6 +5224,12 @@ class _RemoteCommandHandler:
|
|
5044
5224
|
time=msg.time,
|
5045
5225
|
).send(self._chan)
|
5046
5226
|
|
5227
|
+
elif isinstance(msg, _RemoteProtocol.PingResponse):
|
5228
|
+
latency_s = time.time() - msg.time
|
5229
|
+
log.debug('Pong: %0.2f ms %r', latency_s * 1000., msg)
|
5230
|
+
self._last_ping_recv = time.time()
|
5231
|
+
self._ping_in_flight = False
|
5232
|
+
|
5047
5233
|
elif isinstance(msg, _RemoteProtocol.CommandRequest):
|
5048
5234
|
fut = asyncio.create_task(self._handle_command_request(msg))
|
5049
5235
|
self._cmds_by_seq[msg.seq] = _RemoteCommandHandler._Command(
|
@@ -5125,16 +5311,23 @@ class RemoteCommandExecutor(CommandExecutor):
|
|
5125
5311
|
if recv_task is None:
|
5126
5312
|
recv_task = asyncio.create_task(_RemoteProtocol.Message.recv(self._chan))
|
5127
5313
|
|
5128
|
-
done, pending = await asyncio.wait(
|
5129
|
-
|
5130
|
-
|
5131
|
-
|
5132
|
-
|
5314
|
+
done, pending = await asyncio.wait(
|
5315
|
+
[
|
5316
|
+
stop_task,
|
5317
|
+
queue_task,
|
5318
|
+
recv_task,
|
5319
|
+
],
|
5320
|
+
return_when=asyncio.FIRST_COMPLETED,
|
5321
|
+
)
|
5322
|
+
|
5323
|
+
#
|
5133
5324
|
|
5134
5325
|
if queue_task in done:
|
5135
5326
|
req = check.isinstance(queue_task.result(), RemoteCommandExecutor._Request)
|
5136
5327
|
queue_task = None
|
5137
|
-
await self.
|
5328
|
+
await self._handle_queued_request(req)
|
5329
|
+
|
5330
|
+
#
|
5138
5331
|
|
5139
5332
|
if recv_task in done:
|
5140
5333
|
msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
|
@@ -5164,7 +5357,7 @@ class RemoteCommandExecutor(CommandExecutor):
|
|
5164
5357
|
|
5165
5358
|
log.debug('RemoteCommandExecutor loop exited: %r', self)
|
5166
5359
|
|
5167
|
-
async def
|
5360
|
+
async def _handle_queued_request(self, req: _Request) -> None:
|
5168
5361
|
self._reqs_by_seq[req.seq] = req
|
5169
5362
|
await _RemoteProtocol.CommandRequest(
|
5170
5363
|
seq=req.seq,
|
@@ -5178,6 +5371,10 @@ class RemoteCommandExecutor(CommandExecutor):
|
|
5178
5371
|
time=msg.time,
|
5179
5372
|
).send(self._chan)
|
5180
5373
|
|
5374
|
+
elif isinstance(msg, _RemoteProtocol.PingResponse):
|
5375
|
+
latency_s = time.time() - msg.time
|
5376
|
+
log.debug('Pong: %0.2f ms %r', latency_s * 1000., msg)
|
5377
|
+
|
5181
5378
|
elif isinstance(msg, _RemoteProtocol.LogResponse):
|
5182
5379
|
log.info(msg.s)
|
5183
5380
|
|
@@ -5394,22 +5591,25 @@ async def asyncio_subprocess_communicate(
|
|
5394
5591
|
return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
|
5395
5592
|
|
5396
5593
|
|
5397
|
-
|
5398
|
-
|
5399
|
-
|
5400
|
-
async def _asyncio_subprocess_check_run(
|
5594
|
+
async def asyncio_subprocess_run(
|
5401
5595
|
*args: str,
|
5402
5596
|
input: ta.Any = None, # noqa
|
5403
5597
|
timeout: ta.Optional[float] = None,
|
5598
|
+
check: bool = False, # noqa
|
5599
|
+
capture_output: ta.Optional[bool] = None,
|
5404
5600
|
**kwargs: ta.Any,
|
5405
5601
|
) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
|
5602
|
+
if capture_output:
|
5603
|
+
kwargs.setdefault('stdout', subprocess.PIPE)
|
5604
|
+
kwargs.setdefault('stderr', subprocess.PIPE)
|
5605
|
+
|
5406
5606
|
args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
|
5407
5607
|
|
5408
5608
|
proc: asyncio.subprocess.Process
|
5409
5609
|
async with asyncio_subprocess_popen(*args, **kwargs) as proc:
|
5410
5610
|
stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
|
5411
5611
|
|
5412
|
-
if proc.returncode:
|
5612
|
+
if check and proc.returncode:
|
5413
5613
|
raise subprocess.CalledProcessError(
|
5414
5614
|
proc.returncode,
|
5415
5615
|
args,
|
@@ -5420,6 +5620,9 @@ async def _asyncio_subprocess_check_run(
|
|
5420
5620
|
return stdout, stderr
|
5421
5621
|
|
5422
5622
|
|
5623
|
+
##
|
5624
|
+
|
5625
|
+
|
5423
5626
|
async def asyncio_subprocess_check_call(
|
5424
5627
|
*args: str,
|
5425
5628
|
stdout: ta.Any = sys.stderr,
|
@@ -5427,11 +5630,12 @@ async def asyncio_subprocess_check_call(
|
|
5427
5630
|
timeout: ta.Optional[float] = None,
|
5428
5631
|
**kwargs: ta.Any,
|
5429
5632
|
) -> None:
|
5430
|
-
_, _ = await
|
5633
|
+
_, _ = await asyncio_subprocess_run(
|
5431
5634
|
*args,
|
5432
5635
|
stdout=stdout,
|
5433
5636
|
input=input,
|
5434
5637
|
timeout=timeout,
|
5638
|
+
check=True,
|
5435
5639
|
**kwargs,
|
5436
5640
|
)
|
5437
5641
|
|
@@ -5442,11 +5646,12 @@ async def asyncio_subprocess_check_output(
|
|
5442
5646
|
timeout: ta.Optional[float] = None,
|
5443
5647
|
**kwargs: ta.Any,
|
5444
5648
|
) -> bytes:
|
5445
|
-
stdout, stderr = await
|
5649
|
+
stdout, stderr = await asyncio_subprocess_run(
|
5446
5650
|
*args,
|
5447
5651
|
stdout=asyncio.subprocess.PIPE,
|
5448
5652
|
input=input,
|
5449
5653
|
timeout=timeout,
|
5654
|
+
check=True,
|
5450
5655
|
**kwargs,
|
5451
5656
|
)
|
5452
5657
|
|
@@ -5641,9 +5846,6 @@ class SubprocessCommand(Command['SubprocessCommand.Output']):
|
|
5641
5846
|
stderr: ta.Optional[bytes] = None
|
5642
5847
|
|
5643
5848
|
|
5644
|
-
##
|
5645
|
-
|
5646
|
-
|
5647
5849
|
class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
|
5648
5850
|
async def execute(self, cmd: SubprocessCommand) -> SubprocessCommand.Output:
|
5649
5851
|
proc: asyncio.subprocess.Process
|
@@ -5925,6 +6127,102 @@ class SubprocessRemoteSpawning(RemoteSpawning):
|
|
5925
6127
|
pass
|
5926
6128
|
|
5927
6129
|
|
6130
|
+
########################################
|
6131
|
+
# ../system/packages.py
|
6132
|
+
"""
|
6133
|
+
TODO:
|
6134
|
+
- yum/rpm
|
6135
|
+
"""
|
6136
|
+
|
6137
|
+
|
6138
|
+
@dc.dataclass(frozen=True)
|
6139
|
+
class SystemPackage:
|
6140
|
+
name: str
|
6141
|
+
version: ta.Optional[str] = None
|
6142
|
+
|
6143
|
+
|
6144
|
+
class SystemPackageManager(abc.ABC):
|
6145
|
+
@abc.abstractmethod
|
6146
|
+
def update(self) -> ta.Awaitable[None]:
|
6147
|
+
raise NotImplementedError
|
6148
|
+
|
6149
|
+
@abc.abstractmethod
|
6150
|
+
def upgrade(self) -> ta.Awaitable[None]:
|
6151
|
+
raise NotImplementedError
|
6152
|
+
|
6153
|
+
@abc.abstractmethod
|
6154
|
+
def install(self, *packages: SystemPackageOrStr) -> ta.Awaitable[None]:
|
6155
|
+
raise NotImplementedError
|
6156
|
+
|
6157
|
+
@abc.abstractmethod
|
6158
|
+
def query(self, *packages: SystemPackageOrStr) -> ta.Awaitable[ta.Mapping[str, SystemPackage]]:
|
6159
|
+
raise NotImplementedError
|
6160
|
+
|
6161
|
+
|
6162
|
+
class BrewSystemPackageManager(SystemPackageManager):
|
6163
|
+
async def update(self) -> None:
|
6164
|
+
await asyncio_subprocess_check_call('brew', 'update')
|
6165
|
+
|
6166
|
+
async def upgrade(self) -> None:
|
6167
|
+
await asyncio_subprocess_check_call('brew', 'upgrade')
|
6168
|
+
|
6169
|
+
async def install(self, *packages: SystemPackageOrStr) -> None:
|
6170
|
+
es: ta.List[str] = []
|
6171
|
+
for p in packages:
|
6172
|
+
if isinstance(p, SystemPackage):
|
6173
|
+
es.append(p.name + (f'@{p.version}' if p.version is not None else ''))
|
6174
|
+
else:
|
6175
|
+
es.append(p)
|
6176
|
+
await asyncio_subprocess_check_call('brew', 'install', *es)
|
6177
|
+
|
6178
|
+
async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
|
6179
|
+
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
|
6180
|
+
o = await asyncio_subprocess_check_output('brew', 'info', '--json', *pns)
|
6181
|
+
j = json.loads(o.decode())
|
6182
|
+
d: ta.Dict[str, SystemPackage] = {}
|
6183
|
+
for e in j:
|
6184
|
+
if not e['installed']:
|
6185
|
+
continue
|
6186
|
+
d[e['name']] = SystemPackage(
|
6187
|
+
name=e['name'],
|
6188
|
+
version=e['installed'][0]['version'],
|
6189
|
+
)
|
6190
|
+
return d
|
6191
|
+
|
6192
|
+
|
6193
|
+
class AptSystemPackageManager(SystemPackageManager):
|
6194
|
+
_APT_ENV: ta.ClassVar[ta.Mapping[str, str]] = {
|
6195
|
+
'DEBIAN_FRONTEND': 'noninteractive',
|
6196
|
+
}
|
6197
|
+
|
6198
|
+
async def update(self) -> None:
|
6199
|
+
await asyncio_subprocess_check_call('apt', 'update', env={**os.environ, **self._APT_ENV})
|
6200
|
+
|
6201
|
+
async def upgrade(self) -> None:
|
6202
|
+
await asyncio_subprocess_check_call('apt', 'upgrade', '-y', env={**os.environ, **self._APT_ENV})
|
6203
|
+
|
6204
|
+
async def install(self, *packages: SystemPackageOrStr) -> None:
|
6205
|
+
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
|
6206
|
+
await asyncio_subprocess_check_call('apt', 'install', '-y', *pns, env={**os.environ, **self._APT_ENV})
|
6207
|
+
|
6208
|
+
async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
|
6209
|
+
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
|
6210
|
+
cmd = ['dpkg-query', '-W', '-f=${Package}=${Version}\n', *pns]
|
6211
|
+
stdout, stderr = await asyncio_subprocess_run(
|
6212
|
+
*cmd,
|
6213
|
+
capture_output=True,
|
6214
|
+
check=False,
|
6215
|
+
)
|
6216
|
+
d: ta.Dict[str, SystemPackage] = {}
|
6217
|
+
for l in check.not_none(stdout).decode('utf-8').strip().splitlines():
|
6218
|
+
n, v = l.split('=', 1)
|
6219
|
+
d[n] = SystemPackage(
|
6220
|
+
name=n,
|
6221
|
+
version=v,
|
6222
|
+
)
|
6223
|
+
return d
|
6224
|
+
|
6225
|
+
|
5928
6226
|
########################################
|
5929
6227
|
# ../../../omdev/interp/providers.py
|
5930
6228
|
"""
|
@@ -6078,6 +6376,50 @@ class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
|
|
6078
6376
|
yield rce
|
6079
6377
|
|
6080
6378
|
|
6379
|
+
##
|
6380
|
+
|
6381
|
+
|
6382
|
+
class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
|
6383
|
+
def __init__(
|
6384
|
+
self,
|
6385
|
+
*,
|
6386
|
+
msh: ObjMarshalerManager,
|
6387
|
+
local_executor: LocalCommandExecutor,
|
6388
|
+
) -> None:
|
6389
|
+
super().__init__()
|
6390
|
+
|
6391
|
+
self._msh = msh
|
6392
|
+
self._local_executor = local_executor
|
6393
|
+
|
6394
|
+
@contextlib.asynccontextmanager
|
6395
|
+
async def connect(
|
6396
|
+
self,
|
6397
|
+
tgt: RemoteSpawning.Target,
|
6398
|
+
bs: MainBootstrap,
|
6399
|
+
) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
|
6400
|
+
r0, w0 = asyncio_create_bytes_channel()
|
6401
|
+
r1, w1 = asyncio_create_bytes_channel()
|
6402
|
+
|
6403
|
+
remote_chan = RemoteChannelImpl(r0, w1, msh=self._msh)
|
6404
|
+
local_chan = RemoteChannelImpl(r1, w0, msh=self._msh)
|
6405
|
+
|
6406
|
+
rch = _RemoteCommandHandler(
|
6407
|
+
remote_chan,
|
6408
|
+
self._local_executor,
|
6409
|
+
)
|
6410
|
+
rch_task = asyncio.create_task(rch.run()) # noqa
|
6411
|
+
try:
|
6412
|
+
rce: RemoteCommandExecutor
|
6413
|
+
async with contextlib.aclosing(RemoteCommandExecutor(local_chan)) as rce:
|
6414
|
+
await rce.start()
|
6415
|
+
|
6416
|
+
yield rce
|
6417
|
+
|
6418
|
+
finally:
|
6419
|
+
rch.stop()
|
6420
|
+
await rch_task
|
6421
|
+
|
6422
|
+
|
6081
6423
|
########################################
|
6082
6424
|
# ../../../omdev/interp/pyenv.py
|
6083
6425
|
"""
|
@@ -6648,13 +6990,27 @@ def bind_remote(
|
|
6648
6990
|
|
6649
6991
|
inj.bind(SubprocessRemoteSpawning, singleton=True),
|
6650
6992
|
inj.bind(RemoteSpawning, to_key=SubprocessRemoteSpawning),
|
6651
|
-
|
6652
|
-
inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
|
6653
|
-
inj.bind(RemoteExecutionConnector, to_key=PyremoteRemoteExecutionConnector),
|
6654
6993
|
]
|
6655
6994
|
|
6995
|
+
#
|
6996
|
+
|
6997
|
+
if remote_config.use_in_process_remote_executor:
|
6998
|
+
lst.extend([
|
6999
|
+
inj.bind(InProcessRemoteExecutionConnector, singleton=True),
|
7000
|
+
inj.bind(RemoteExecutionConnector, to_key=InProcessRemoteExecutionConnector),
|
7001
|
+
])
|
7002
|
+
else:
|
7003
|
+
lst.extend([
|
7004
|
+
inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
|
7005
|
+
inj.bind(RemoteExecutionConnector, to_key=PyremoteRemoteExecutionConnector),
|
7006
|
+
])
|
7007
|
+
|
7008
|
+
#
|
7009
|
+
|
6656
7010
|
if (pf := remote_config.payload_file) is not None:
|
6657
|
-
lst.append(inj.bind(pf,
|
7011
|
+
lst.append(inj.bind(pf, key=RemoteExecutionPayloadFile))
|
7012
|
+
|
7013
|
+
#
|
6658
7014
|
|
6659
7015
|
return inj.as_bindings(*lst)
|
6660
7016
|
|
@@ -6776,9 +7132,6 @@ class InterpCommand(Command['InterpCommand.Output']):
|
|
6776
7132
|
opts: InterpOpts
|
6777
7133
|
|
6778
7134
|
|
6779
|
-
##
|
6780
|
-
|
6781
|
-
|
6782
7135
|
class InterpCommandExecutor(CommandExecutor[InterpCommand, InterpCommand.Output]):
|
6783
7136
|
async def execute(self, cmd: InterpCommand) -> InterpCommand.Output:
|
6784
7137
|
i = InterpSpecifier.parse(check.not_none(cmd.spec))
|
@@ -6906,6 +7259,48 @@ def bind_deploy(
|
|
6906
7259
|
return inj.as_bindings(*lst)
|
6907
7260
|
|
6908
7261
|
|
7262
|
+
########################################
|
7263
|
+
# ../system/inject.py
|
7264
|
+
|
7265
|
+
|
7266
|
+
def bind_system(
|
7267
|
+
*,
|
7268
|
+
system_config: SystemConfig,
|
7269
|
+
) -> InjectorBindings:
|
7270
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
7271
|
+
inj.bind(system_config),
|
7272
|
+
]
|
7273
|
+
|
7274
|
+
#
|
7275
|
+
|
7276
|
+
platform = system_config.platform or sys.platform
|
7277
|
+
lst.append(inj.bind(platform, key=SystemPlatform))
|
7278
|
+
|
7279
|
+
#
|
7280
|
+
|
7281
|
+
if platform == 'linux':
|
7282
|
+
lst.extend([
|
7283
|
+
inj.bind(AptSystemPackageManager, singleton=True),
|
7284
|
+
inj.bind(SystemPackageManager, to_key=AptSystemPackageManager),
|
7285
|
+
])
|
7286
|
+
|
7287
|
+
elif platform == 'darwin':
|
7288
|
+
lst.extend([
|
7289
|
+
inj.bind(BrewSystemPackageManager, singleton=True),
|
7290
|
+
inj.bind(SystemPackageManager, to_key=BrewSystemPackageManager),
|
7291
|
+
])
|
7292
|
+
|
7293
|
+
#
|
7294
|
+
|
7295
|
+
lst.extend([
|
7296
|
+
bind_command(CheckSystemPackageCommand, CheckSystemPackageCommandExecutor),
|
7297
|
+
])
|
7298
|
+
|
7299
|
+
#
|
7300
|
+
|
7301
|
+
return inj.as_bindings(*lst)
|
7302
|
+
|
7303
|
+
|
6909
7304
|
########################################
|
6910
7305
|
# ../inject.py
|
6911
7306
|
|
@@ -6917,6 +7312,7 @@ def bind_main(
|
|
6917
7312
|
*,
|
6918
7313
|
main_config: MainConfig,
|
6919
7314
|
remote_config: RemoteConfig,
|
7315
|
+
system_config: SystemConfig,
|
6920
7316
|
) -> InjectorBindings:
|
6921
7317
|
lst: ta.List[InjectorBindingOrBindings] = [
|
6922
7318
|
inj.bind(main_config),
|
@@ -6925,11 +7321,15 @@ def bind_main(
|
|
6925
7321
|
main_config=main_config,
|
6926
7322
|
),
|
6927
7323
|
|
7324
|
+
bind_deploy(),
|
7325
|
+
|
6928
7326
|
bind_remote(
|
6929
7327
|
remote_config=remote_config,
|
6930
7328
|
),
|
6931
7329
|
|
6932
|
-
|
7330
|
+
bind_system(
|
7331
|
+
system_config=system_config,
|
7332
|
+
),
|
6933
7333
|
]
|
6934
7334
|
|
6935
7335
|
#
|
@@ -6964,6 +7364,7 @@ def main_bootstrap(bs: MainBootstrap) -> Injector:
|
|
6964
7364
|
injector = inj.create_injector(bind_main( # noqa
|
6965
7365
|
main_config=bs.main_config,
|
6966
7366
|
remote_config=bs.remote_config,
|
7367
|
+
system_config=bs.system_config,
|
6967
7368
|
))
|
6968
7369
|
|
6969
7370
|
return injector
|
@@ -6975,7 +7376,7 @@ def main_bootstrap(bs: MainBootstrap) -> Injector:
|
|
6975
7376
|
|
6976
7377
|
class MainCli(ArgparseCli):
|
6977
7378
|
@argparse_command(
|
6978
|
-
argparse_arg('--
|
7379
|
+
argparse_arg('--_payload-file'),
|
6979
7380
|
|
6980
7381
|
argparse_arg('-s', '--shell'),
|
6981
7382
|
argparse_arg('-q', '--shell-quote', action='store_true'),
|
@@ -6993,10 +7394,7 @@ class MainCli(ArgparseCli):
|
|
6993
7394
|
|
6994
7395
|
argparse_arg('command', nargs='+'),
|
6995
7396
|
)
|
6996
|
-
def run(self) -> None:
|
6997
|
-
asyncio.run(self._async_run())
|
6998
|
-
|
6999
|
-
async def _async_run(self) -> None:
|
7397
|
+
async def run(self) -> None:
|
7000
7398
|
bs = MainBootstrap(
|
7001
7399
|
main_config=MainConfig(
|
7002
7400
|
log_level='DEBUG' if self.args.debug else 'INFO',
|
@@ -7005,7 +7403,7 @@ class MainCli(ArgparseCli):
|
|
7005
7403
|
),
|
7006
7404
|
|
7007
7405
|
remote_config=RemoteConfig(
|
7008
|
-
payload_file=self.args.
|
7406
|
+
payload_file=self.args._payload_file, # noqa
|
7009
7407
|
|
7010
7408
|
pycharm_remote_debug=PycharmRemoteDebug(
|
7011
7409
|
port=self.args.pycharm_debug_port,
|
@@ -7014,6 +7412,8 @@ class MainCli(ArgparseCli):
|
|
7014
7412
|
) if self.args.pycharm_debug_port is not None else None,
|
7015
7413
|
|
7016
7414
|
timebomb_delay_s=self.args.remote_timebomb_delay_s,
|
7415
|
+
|
7416
|
+
use_in_process_remote_executor=True,
|
7017
7417
|
),
|
7018
7418
|
)
|
7019
7419
|
|
@@ -7068,7 +7468,7 @@ class MainCli(ArgparseCli):
|
|
7068
7468
|
|
7069
7469
|
|
7070
7470
|
def _main() -> None:
|
7071
|
-
MainCli().
|
7471
|
+
sys.exit(asyncio.run(MainCli().async_cli_run()))
|
7072
7472
|
|
7073
7473
|
|
7074
7474
|
if __name__ == '__main__':
|