ominfra 0.0.0.dev152__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/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/lite/asyncio/asyncio.py
71
+ # ../../omlish/asyncs/asyncio/timeouts.py
72
72
  AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
73
73
 
74
74
  # ../../omlish/lite/cached.py
@@ -105,6 +105,9 @@ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
105
105
  # ../../omlish/lite/subprocesses.py
106
106
  SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
107
107
 
108
+ # system/packages.py
109
+ SystemPackageOrStr = ta.Union['SystemPackage', str]
110
+
108
111
 
109
112
  ########################################
110
113
  # ../../../omdev/packaging/versions.py
@@ -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/lite/asyncio/asyncio.py
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(
@@ -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
@@ -4606,6 +4665,8 @@ class MainBootstrap:
4606
4665
 
4607
4666
  remote_config: RemoteConfig = RemoteConfig()
4608
4667
 
4668
+ system_config: SystemConfig = SystemConfig()
4669
+
4609
4670
 
4610
4671
  ########################################
4611
4672
  # ../commands/execution.py
@@ -4655,7 +4716,7 @@ def install_command_marshaling(
4655
4716
 
4656
4717
 
4657
4718
  ########################################
4658
- # ../deploy/command.py
4719
+ # ../deploy/commands.py
4659
4720
 
4660
4721
 
4661
4722
  ##
@@ -4668,9 +4729,6 @@ class DeployCommand(Command['DeployCommand.Output']):
4668
4729
  pass
4669
4730
 
4670
4731
 
4671
- ##
4672
-
4673
-
4674
4732
  class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
4675
4733
  async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
4676
4734
  log.info('Deploying!')
@@ -4769,6 +4827,27 @@ class RemoteChannelImpl(RemoteChannel):
4769
4827
  return await self._recv_obj(ty)
4770
4828
 
4771
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
+
4772
4851
  ########################################
4773
4852
  # ../../../omlish/lite/subprocesses.py
4774
4853
 
@@ -4955,6 +5034,10 @@ def subprocess_close(
4955
5034
 
4956
5035
  ########################################
4957
5036
  # ../remote/execution.py
5037
+ """
5038
+ TODO:
5039
+ - sequence all messages
5040
+ """
4958
5041
 
4959
5042
 
4960
5043
  ##
@@ -5033,38 +5116,80 @@ class _RemoteLogHandler(logging.Handler):
5033
5116
 
5034
5117
 
5035
5118
  class _RemoteCommandHandler:
5119
+ DEFAULT_PING_INTERVAL_S: float = 3.
5120
+
5036
5121
  def __init__(
5037
5122
  self,
5038
5123
  chan: RemoteChannel,
5039
5124
  executor: CommandExecutor,
5040
5125
  *,
5041
5126
  stop: ta.Optional[asyncio.Event] = None,
5127
+ ping_interval_s: float = DEFAULT_PING_INTERVAL_S,
5042
5128
  ) -> None:
5043
5129
  super().__init__()
5044
5130
 
5045
5131
  self._chan = chan
5046
5132
  self._executor = executor
5047
5133
  self._stop = stop if stop is not None else asyncio.Event()
5134
+ self._ping_interval_s = ping_interval_s
5048
5135
 
5049
5136
  self._cmds_by_seq: ta.Dict[int, _RemoteCommandHandler._Command] = {}
5050
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
+
5051
5145
  @dc.dataclass(frozen=True)
5052
5146
  class _Command:
5053
5147
  req: _RemoteProtocol.CommandRequest
5054
5148
  fut: asyncio.Future
5055
5149
 
5056
5150
  async def run(self) -> None:
5151
+ log.debug('_RemoteCommandHandler loop start: %r', self)
5152
+
5057
5153
  stop_task = asyncio.create_task(self._stop.wait())
5058
5154
  recv_task: ta.Optional[asyncio.Task] = None
5059
5155
 
5060
5156
  while not self._stop.is_set():
5061
5157
  if recv_task is None:
5062
- recv_task = asyncio.create_task(_RemoteProtocol.Request.recv(self._chan))
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
+ )
5178
+
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)
5063
5191
 
5064
- done, pending = await asyncio.wait([
5065
- stop_task,
5066
- recv_task,
5067
- ], return_when=asyncio.FIRST_COMPLETED)
5192
+ #
5068
5193
 
5069
5194
  if recv_task in done:
5070
5195
  msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
@@ -5078,6 +5203,20 @@ class _RemoteCommandHandler:
5078
5203
 
5079
5204
  await self._handle_message(msg)
5080
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
+
5081
5220
  async def _handle_message(self, msg: _RemoteProtocol.Message) -> None:
5082
5221
  if isinstance(msg, _RemoteProtocol.PingRequest):
5083
5222
  log.debug('Ping: %r', msg)
@@ -5085,6 +5224,12 @@ class _RemoteCommandHandler:
5085
5224
  time=msg.time,
5086
5225
  ).send(self._chan)
5087
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
+
5088
5233
  elif isinstance(msg, _RemoteProtocol.CommandRequest):
5089
5234
  fut = asyncio.create_task(self._handle_command_request(msg))
5090
5235
  self._cmds_by_seq[msg.seq] = _RemoteCommandHandler._Command(
@@ -5166,16 +5311,23 @@ class RemoteCommandExecutor(CommandExecutor):
5166
5311
  if recv_task is None:
5167
5312
  recv_task = asyncio.create_task(_RemoteProtocol.Message.recv(self._chan))
5168
5313
 
5169
- done, pending = await asyncio.wait([
5170
- stop_task,
5171
- queue_task,
5172
- recv_task,
5173
- ], return_when=asyncio.FIRST_COMPLETED)
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
+ #
5174
5324
 
5175
5325
  if queue_task in done:
5176
5326
  req = check.isinstance(queue_task.result(), RemoteCommandExecutor._Request)
5177
5327
  queue_task = None
5178
- await self._handle_request(req)
5328
+ await self._handle_queued_request(req)
5329
+
5330
+ #
5179
5331
 
5180
5332
  if recv_task in done:
5181
5333
  msg: ta.Optional[_RemoteProtocol.Message] = check.isinstance(
@@ -5205,7 +5357,7 @@ class RemoteCommandExecutor(CommandExecutor):
5205
5357
 
5206
5358
  log.debug('RemoteCommandExecutor loop exited: %r', self)
5207
5359
 
5208
- async def _handle_request(self, req: _Request) -> None:
5360
+ async def _handle_queued_request(self, req: _Request) -> None:
5209
5361
  self._reqs_by_seq[req.seq] = req
5210
5362
  await _RemoteProtocol.CommandRequest(
5211
5363
  seq=req.seq,
@@ -5219,6 +5371,10 @@ class RemoteCommandExecutor(CommandExecutor):
5219
5371
  time=msg.time,
5220
5372
  ).send(self._chan)
5221
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
+
5222
5378
  elif isinstance(msg, _RemoteProtocol.LogResponse):
5223
5379
  log.info(msg.s)
5224
5380
 
@@ -5435,22 +5591,25 @@ async def asyncio_subprocess_communicate(
5435
5591
  return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
5436
5592
 
5437
5593
 
5438
- ##
5439
-
5440
-
5441
- async def _asyncio_subprocess_check_run(
5594
+ async def asyncio_subprocess_run(
5442
5595
  *args: str,
5443
5596
  input: ta.Any = None, # noqa
5444
5597
  timeout: ta.Optional[float] = None,
5598
+ check: bool = False, # noqa
5599
+ capture_output: ta.Optional[bool] = None,
5445
5600
  **kwargs: ta.Any,
5446
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
+
5447
5606
  args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
5448
5607
 
5449
5608
  proc: asyncio.subprocess.Process
5450
5609
  async with asyncio_subprocess_popen(*args, **kwargs) as proc:
5451
5610
  stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
5452
5611
 
5453
- if proc.returncode:
5612
+ if check and proc.returncode:
5454
5613
  raise subprocess.CalledProcessError(
5455
5614
  proc.returncode,
5456
5615
  args,
@@ -5461,6 +5620,9 @@ async def _asyncio_subprocess_check_run(
5461
5620
  return stdout, stderr
5462
5621
 
5463
5622
 
5623
+ ##
5624
+
5625
+
5464
5626
  async def asyncio_subprocess_check_call(
5465
5627
  *args: str,
5466
5628
  stdout: ta.Any = sys.stderr,
@@ -5468,11 +5630,12 @@ async def asyncio_subprocess_check_call(
5468
5630
  timeout: ta.Optional[float] = None,
5469
5631
  **kwargs: ta.Any,
5470
5632
  ) -> None:
5471
- _, _ = await _asyncio_subprocess_check_run(
5633
+ _, _ = await asyncio_subprocess_run(
5472
5634
  *args,
5473
5635
  stdout=stdout,
5474
5636
  input=input,
5475
5637
  timeout=timeout,
5638
+ check=True,
5476
5639
  **kwargs,
5477
5640
  )
5478
5641
 
@@ -5483,11 +5646,12 @@ async def asyncio_subprocess_check_output(
5483
5646
  timeout: ta.Optional[float] = None,
5484
5647
  **kwargs: ta.Any,
5485
5648
  ) -> bytes:
5486
- stdout, stderr = await _asyncio_subprocess_check_run(
5649
+ stdout, stderr = await asyncio_subprocess_run(
5487
5650
  *args,
5488
5651
  stdout=asyncio.subprocess.PIPE,
5489
5652
  input=input,
5490
5653
  timeout=timeout,
5654
+ check=True,
5491
5655
  **kwargs,
5492
5656
  )
5493
5657
 
@@ -5682,9 +5846,6 @@ class SubprocessCommand(Command['SubprocessCommand.Output']):
5682
5846
  stderr: ta.Optional[bytes] = None
5683
5847
 
5684
5848
 
5685
- ##
5686
-
5687
-
5688
5849
  class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
5689
5850
  async def execute(self, cmd: SubprocessCommand) -> SubprocessCommand.Output:
5690
5851
  proc: asyncio.subprocess.Process
@@ -5966,6 +6127,102 @@ class SubprocessRemoteSpawning(RemoteSpawning):
5966
6127
  pass
5967
6128
 
5968
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
+
5969
6226
  ########################################
5970
6227
  # ../../../omdev/interp/providers.py
5971
6228
  """
@@ -6119,6 +6376,50 @@ class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
6119
6376
  yield rce
6120
6377
 
6121
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
+
6122
6423
  ########################################
6123
6424
  # ../../../omdev/interp/pyenv.py
6124
6425
  """
@@ -6689,13 +6990,27 @@ def bind_remote(
6689
6990
 
6690
6991
  inj.bind(SubprocessRemoteSpawning, singleton=True),
6691
6992
  inj.bind(RemoteSpawning, to_key=SubprocessRemoteSpawning),
6692
-
6693
- inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
6694
- inj.bind(RemoteExecutionConnector, to_key=PyremoteRemoteExecutionConnector),
6695
6993
  ]
6696
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
+
6697
7010
  if (pf := remote_config.payload_file) is not None:
6698
- lst.append(inj.bind(pf, to_key=RemoteExecutionPayloadFile))
7011
+ lst.append(inj.bind(pf, key=RemoteExecutionPayloadFile))
7012
+
7013
+ #
6699
7014
 
6700
7015
  return inj.as_bindings(*lst)
6701
7016
 
@@ -6817,9 +7132,6 @@ class InterpCommand(Command['InterpCommand.Output']):
6817
7132
  opts: InterpOpts
6818
7133
 
6819
7134
 
6820
- ##
6821
-
6822
-
6823
7135
  class InterpCommandExecutor(CommandExecutor[InterpCommand, InterpCommand.Output]):
6824
7136
  async def execute(self, cmd: InterpCommand) -> InterpCommand.Output:
6825
7137
  i = InterpSpecifier.parse(check.not_none(cmd.spec))
@@ -6947,6 +7259,48 @@ def bind_deploy(
6947
7259
  return inj.as_bindings(*lst)
6948
7260
 
6949
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
+
6950
7304
  ########################################
6951
7305
  # ../inject.py
6952
7306
 
@@ -6958,6 +7312,7 @@ def bind_main(
6958
7312
  *,
6959
7313
  main_config: MainConfig,
6960
7314
  remote_config: RemoteConfig,
7315
+ system_config: SystemConfig,
6961
7316
  ) -> InjectorBindings:
6962
7317
  lst: ta.List[InjectorBindingOrBindings] = [
6963
7318
  inj.bind(main_config),
@@ -6966,11 +7321,15 @@ def bind_main(
6966
7321
  main_config=main_config,
6967
7322
  ),
6968
7323
 
7324
+ bind_deploy(),
7325
+
6969
7326
  bind_remote(
6970
7327
  remote_config=remote_config,
6971
7328
  ),
6972
7329
 
6973
- bind_deploy(),
7330
+ bind_system(
7331
+ system_config=system_config,
7332
+ ),
6974
7333
  ]
6975
7334
 
6976
7335
  #
@@ -7005,6 +7364,7 @@ def main_bootstrap(bs: MainBootstrap) -> Injector:
7005
7364
  injector = inj.create_injector(bind_main( # noqa
7006
7365
  main_config=bs.main_config,
7007
7366
  remote_config=bs.remote_config,
7367
+ system_config=bs.system_config,
7008
7368
  ))
7009
7369
 
7010
7370
  return injector
@@ -7052,6 +7412,8 @@ class MainCli(ArgparseCli):
7052
7412
  ) if self.args.pycharm_debug_port is not None else None,
7053
7413
 
7054
7414
  timebomb_delay_s=self.args.remote_timebomb_delay_s,
7415
+
7416
+ use_in_process_remote_executor=True,
7055
7417
  ),
7056
7418
  )
7057
7419