ominfra 0.0.0.dev141__py3-none-any.whl → 0.0.0.dev143__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.
Files changed (31) hide show
  1. ominfra/manage/__init__.py +10 -0
  2. ominfra/manage/__main__.py +4 -0
  3. ominfra/manage/bootstrap.py +11 -0
  4. ominfra/manage/bootstrap_.py +18 -0
  5. ominfra/manage/commands/base.py +133 -1
  6. ominfra/manage/commands/execution.py +23 -0
  7. ominfra/manage/commands/inject.py +122 -0
  8. ominfra/manage/commands/marshal.py +26 -0
  9. ominfra/manage/commands/subprocess.py +18 -17
  10. ominfra/manage/config.py +10 -0
  11. ominfra/manage/inject.py +55 -0
  12. ominfra/manage/main.py +54 -132
  13. ominfra/manage/marshal.py +12 -0
  14. ominfra/manage/remote/__init__.py +0 -0
  15. ominfra/manage/remote/channel.py +52 -0
  16. ominfra/manage/remote/config.py +12 -0
  17. ominfra/manage/remote/execution.py +193 -0
  18. ominfra/manage/remote/inject.py +29 -0
  19. ominfra/manage/{payload.py → remote/payload.py} +7 -1
  20. ominfra/manage/{spawning.py → remote/spawning.py} +31 -35
  21. ominfra/pyremote.py +53 -14
  22. ominfra/scripts/journald2aws.py +216 -131
  23. ominfra/scripts/manage.py +2180 -452
  24. ominfra/scripts/supervisor.py +203 -130
  25. ominfra/supervisor/inject.py +2 -1
  26. {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/METADATA +3 -3
  27. {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/RECORD +31 -17
  28. {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/LICENSE +0 -0
  29. {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/WHEEL +0 -0
  30. {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/entry_points.txt +0 -0
  31. {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/top_level.txt +0 -0
ominfra/manage/main.py CHANGED
@@ -5,101 +5,18 @@
5
5
  manage.py -s 'docker run -i python:3.12'
6
6
  manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
7
7
  """
8
- import json
9
- import struct
10
- import typing as ta
11
-
12
- from omlish.lite.cached import static_init
13
- from omlish.lite.json import json_dumps_compact
14
- from omlish.lite.marshal import PolymorphicObjMarshaler
15
- from omlish.lite.marshal import get_obj_marshaler
16
- from omlish.lite.marshal import marshal_obj
17
- from omlish.lite.marshal import register_opj_marshaler
18
- from omlish.lite.marshal import unmarshal_obj
19
-
20
- from ..pyremote import PyremoteBootstrapDriver
21
- from ..pyremote import PyremoteBootstrapOptions
22
- from ..pyremote import pyremote_bootstrap_finalize
23
- from ..pyremote import pyremote_build_bootstrap_cmd
24
- from .commands.base import Command
25
- from .commands.subprocess import SubprocessCommand
26
- from .commands.subprocess import SubprocessCommandExecutor
27
- from .payload import get_payload_src
28
- from .spawning import PySpawner
29
-
30
-
31
- ##
32
-
33
-
34
- _COMMAND_TYPES = {
35
- 'subprocess': SubprocessCommand,
36
- }
37
-
38
-
39
- @static_init
40
- def _register_command_marshaling() -> None:
41
- for fn in [
42
- lambda c: c,
43
- lambda c: c.Output,
44
- ]:
45
- register_opj_marshaler(
46
- fn(Command),
47
- PolymorphicObjMarshaler.of([
48
- PolymorphicObjMarshaler.Impl(
49
- fn(cty),
50
- k,
51
- get_obj_marshaler(fn(cty)),
52
- )
53
- for k, cty in _COMMAND_TYPES.items()
54
- ]),
55
- )
56
-
57
-
58
- ##
59
-
60
-
61
- def _send_obj(f: ta.IO, o: ta.Any, ty: ta.Any = None) -> None:
62
- j = json_dumps_compact(marshal_obj(o, ty))
63
- d = j.encode('utf-8')
64
-
65
- f.write(struct.pack('<I', len(d)))
66
- f.write(d)
67
- f.flush()
68
-
69
-
70
- def _recv_obj(f: ta.IO, ty: ta.Any) -> ta.Any:
71
- d = f.read(4)
72
- if not d:
73
- return None
74
- if len(d) != 4:
75
- raise EOFError
76
-
77
- sz = struct.unpack('<I', d)[0]
78
- d = f.read(sz)
79
- if len(d) != sz:
80
- raise EOFError
81
-
82
- j = json.loads(d.decode('utf-8'))
83
- return unmarshal_obj(j, ty)
84
-
85
-
86
- ##
8
+ from omlish.lite.logs import log # noqa
9
+ from omlish.lite.marshal import ObjMarshalOptions
10
+ from omlish.lite.marshal import ObjMarshalerManager
11
+ from omlish.lite.pycharm import PycharmRemoteDebug
87
12
 
88
-
89
- def _remote_main() -> None:
90
- rt = pyremote_bootstrap_finalize() # noqa
91
-
92
- while True:
93
- i = _recv_obj(rt.input, Command)
94
- if i is None:
95
- break
96
-
97
- if isinstance(i, SubprocessCommand):
98
- o = SubprocessCommandExecutor().execute(i) # noqa
99
- else:
100
- raise TypeError(i)
101
-
102
- _send_obj(rt.output, o, Command.Output)
13
+ from .bootstrap import MainBootstrap
14
+ from .bootstrap_ import main_bootstrap
15
+ from .commands.subprocess import SubprocessCommand
16
+ from .config import MainConfig
17
+ from .remote.config import RemoteConfig
18
+ from .remote.execution import RemoteExecution
19
+ from .remote.spawning import RemoteSpawning
103
20
 
104
21
 
105
22
  ##
@@ -110,65 +27,70 @@ def _main() -> None:
110
27
 
111
28
  parser = argparse.ArgumentParser()
112
29
 
30
+ parser.add_argument('--_payload-file')
31
+
113
32
  parser.add_argument('-s', '--shell')
114
33
  parser.add_argument('-q', '--shell-quote', action='store_true')
115
34
  parser.add_argument('--python', default='python3')
35
+
36
+ parser.add_argument('--pycharm-debug-port', type=int)
37
+ parser.add_argument('--pycharm-debug-host')
38
+ parser.add_argument('--pycharm-debug-version')
39
+
116
40
  parser.add_argument('--debug', action='store_true')
117
- parser.add_argument('--_payload-file')
41
+
42
+ parser.add_argument('command', nargs='+')
118
43
 
119
44
  args = parser.parse_args()
120
45
 
121
46
  #
122
47
 
123
- payload_src = get_payload_src(file=args._payload_file) # noqa
48
+ bs = MainBootstrap(
49
+ main_config=MainConfig(
50
+ log_level='DEBUG' if args.debug else 'INFO',
124
51
 
125
- #
52
+ debug=bool(args.debug),
53
+ ),
54
+
55
+ remote_config=RemoteConfig(
56
+ payload_file=args._payload_file, # noqa
126
57
 
127
- remote_src = '\n\n'.join([
128
- '__name__ = "__remote__"',
129
- payload_src,
130
- '_remote_main()',
131
- ])
58
+ pycharm_remote_debug=PycharmRemoteDebug(
59
+ port=args.pycharm_debug_port,
60
+ host=args.pycharm_debug_host,
61
+ install_version=args.pycharm_debug_version,
62
+ ) if args.pycharm_debug_port is not None else None,
63
+ ),
64
+ )
65
+
66
+ injector = main_bootstrap(
67
+ bs,
68
+ )
132
69
 
133
70
  #
134
71
 
135
- bs_src = pyremote_build_bootstrap_cmd(__package__ or 'manage')
72
+ cmds = [
73
+ SubprocessCommand([c])
74
+ for c in args.command
75
+ ]
136
76
 
137
77
  #
138
78
 
139
- spawner = PySpawner(
140
- bs_src,
79
+ tgt = RemoteSpawning.Target(
141
80
  shell=args.shell,
142
81
  shell_quote=args.shell_quote,
143
82
  python=args.python,
144
83
  )
145
- with spawner.spawn() as proc:
146
- res = PyremoteBootstrapDriver( # noqa
147
- remote_src,
148
- PyremoteBootstrapOptions(
149
- debug=args.debug,
150
- ),
151
- ).run(proc.stdin, proc.stdout)
152
- # print(res)
153
-
154
- #
155
-
156
- for ci in [
157
- SubprocessCommand(
158
- args=['python3', '-'],
159
- input=b'print(1)\n',
160
- capture_stdout=True,
161
- ),
162
- SubprocessCommand(
163
- args=['uname'],
164
- capture_stdout=True,
165
- ),
166
- ]:
167
- _send_obj(proc.stdin, ci, Command)
168
-
169
- o = _recv_obj(proc.stdout, Command.Output)
170
-
171
- print(o)
84
+
85
+ with injector[RemoteExecution].connect(tgt, bs) as rce:
86
+ for cmd in cmds:
87
+ r = rce.try_execute(cmd)
88
+
89
+ print(injector[ObjMarshalerManager].marshal_obj(r, opts=ObjMarshalOptions(raw_bytes=True)))
90
+
91
+ #
92
+
93
+ print('Success')
172
94
 
173
95
 
174
96
  if __name__ == '__main__':
@@ -0,0 +1,12 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from omlish.lite.marshal import ObjMarshalerManager
5
+
6
+
7
+ @dc.dataclass(frozen=True)
8
+ class ObjMarshalerInstaller:
9
+ fn: ta.Callable[[ObjMarshalerManager], None]
10
+
11
+
12
+ ObjMarshalerInstallers = ta.NewType('ObjMarshalerInstallers', ta.Sequence[ObjMarshalerInstaller])
File without changes
@@ -0,0 +1,52 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import json
3
+ import struct
4
+ import typing as ta
5
+
6
+ from omlish.lite.json import json_dumps_compact
7
+ from omlish.lite.marshal import OBJ_MARSHALER_MANAGER
8
+ from omlish.lite.marshal import ObjMarshalerManager
9
+
10
+
11
+ T = ta.TypeVar('T')
12
+
13
+
14
+ class RemoteChannel:
15
+ def __init__(
16
+ self,
17
+ input: ta.IO, # noqa
18
+ output: ta.IO,
19
+ *,
20
+ msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
21
+ ) -> None:
22
+ super().__init__()
23
+
24
+ self._input = input
25
+ self._output = output
26
+ self._msh = msh
27
+
28
+ def set_marshaler(self, msh: ObjMarshalerManager) -> None:
29
+ self._msh = msh
30
+
31
+ def send_obj(self, o: ta.Any, ty: ta.Any = None) -> None:
32
+ j = json_dumps_compact(self._msh.marshal_obj(o, ty))
33
+ d = j.encode('utf-8')
34
+
35
+ self._output.write(struct.pack('<I', len(d)))
36
+ self._output.write(d)
37
+ self._output.flush()
38
+
39
+ def recv_obj(self, ty: ta.Type[T]) -> ta.Optional[T]:
40
+ d = self._input.read(4)
41
+ if not d:
42
+ return None
43
+ if len(d) != 4:
44
+ raise EOFError
45
+
46
+ sz = struct.unpack('<I', d)[0]
47
+ d = self._input.read(sz)
48
+ if len(d) != sz:
49
+ raise EOFError
50
+
51
+ j = json.loads(d.decode('utf-8'))
52
+ return self._msh.unmarshal_obj(j, ty)
@@ -0,0 +1,12 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+ from omlish.lite.pycharm import PycharmRemoteDebug
6
+
7
+
8
+ @dc.dataclass(frozen=True)
9
+ class RemoteConfig:
10
+ payload_file: ta.Optional[str] = None
11
+
12
+ pycharm_remote_debug: ta.Optional[PycharmRemoteDebug] = None
@@ -0,0 +1,193 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import contextlib
3
+ import dataclasses as dc
4
+ import logging
5
+ import typing as ta
6
+
7
+ from omlish.lite.cached import cached_nullary
8
+ from omlish.lite.check import check_not_none
9
+ from omlish.lite.logs import log
10
+ from omlish.lite.marshal import ObjMarshalerManager
11
+ from omlish.lite.pycharm import pycharm_debug_connect
12
+
13
+ from ...pyremote import PyremoteBootstrapDriver
14
+ from ...pyremote import PyremoteBootstrapOptions
15
+ from ...pyremote import pyremote_bootstrap_finalize
16
+ from ...pyremote import pyremote_build_bootstrap_cmd
17
+ from ..bootstrap import MainBootstrap
18
+ from ..commands.base import Command
19
+ from ..commands.base import CommandException
20
+ from ..commands.base import CommandExecutor
21
+ from ..commands.base import CommandOutputOrException
22
+ from ..commands.base import CommandOutputOrExceptionData
23
+ from .channel import RemoteChannel
24
+ from .payload import RemoteExecutionPayloadFile
25
+ from .payload import get_remote_payload_src
26
+ from .spawning import RemoteSpawning
27
+
28
+
29
+ if ta.TYPE_CHECKING:
30
+ from ..bootstrap_ import main_bootstrap
31
+ else:
32
+ main_bootstrap: ta.Any = None
33
+
34
+
35
+ ##
36
+
37
+
38
+ def _remote_execution_main() -> None:
39
+ rt = pyremote_bootstrap_finalize() # noqa
40
+
41
+ chan = RemoteChannel(
42
+ rt.input,
43
+ rt.output,
44
+ )
45
+
46
+ bs = check_not_none(chan.recv_obj(MainBootstrap))
47
+
48
+ if (prd := bs.remote_config.pycharm_remote_debug) is not None:
49
+ pycharm_debug_connect(prd)
50
+
51
+ injector = main_bootstrap(bs)
52
+
53
+ chan.set_marshaler(injector[ObjMarshalerManager])
54
+
55
+ ce = injector[CommandExecutor]
56
+
57
+ while True:
58
+ i = chan.recv_obj(Command)
59
+ if i is None:
60
+ break
61
+
62
+ r = ce.try_execute(
63
+ i,
64
+ log=log,
65
+ omit_exc_object=True,
66
+ )
67
+
68
+ chan.send_obj(r)
69
+
70
+
71
+ ##
72
+
73
+
74
+ @dc.dataclass()
75
+ class RemoteCommandError(Exception):
76
+ e: CommandException
77
+
78
+
79
+ class RemoteCommandExecutor(CommandExecutor):
80
+ def __init__(self, chan: RemoteChannel) -> None:
81
+ super().__init__()
82
+
83
+ self._chan = chan
84
+
85
+ def _remote_execute(self, cmd: Command) -> CommandOutputOrException:
86
+ self._chan.send_obj(cmd, Command)
87
+
88
+ if (r := self._chan.recv_obj(CommandOutputOrExceptionData)) is None:
89
+ raise EOFError
90
+
91
+ return r
92
+
93
+ # @ta.override
94
+ def execute(self, cmd: Command) -> Command.Output:
95
+ r = self._remote_execute(cmd)
96
+ if (e := r.exception) is not None:
97
+ raise RemoteCommandError(e)
98
+ else:
99
+ return check_not_none(r.output)
100
+
101
+ # @ta.override
102
+ def try_execute(
103
+ self,
104
+ cmd: Command,
105
+ *,
106
+ log: ta.Optional[logging.Logger] = None,
107
+ omit_exc_object: bool = False,
108
+ ) -> CommandOutputOrException:
109
+ try:
110
+ r = self._remote_execute(cmd)
111
+
112
+ except Exception as e: # noqa
113
+ if log is not None:
114
+ log.exception('Exception executing remote command: %r', type(cmd))
115
+
116
+ return CommandOutputOrExceptionData(exception=CommandException.of(
117
+ e,
118
+ omit_exc_object=omit_exc_object,
119
+ cmd=cmd,
120
+ ))
121
+
122
+ else:
123
+ return r
124
+
125
+
126
+ ##
127
+
128
+
129
+ class RemoteExecution:
130
+ def __init__(
131
+ self,
132
+ *,
133
+ spawning: RemoteSpawning,
134
+ msh: ObjMarshalerManager,
135
+ payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
136
+ ) -> None:
137
+ super().__init__()
138
+
139
+ self._spawning = spawning
140
+ self._msh = msh
141
+ self._payload_file = payload_file
142
+
143
+ #
144
+
145
+ @cached_nullary
146
+ def _payload_src(self) -> str:
147
+ return get_remote_payload_src(file=self._payload_file)
148
+
149
+ @cached_nullary
150
+ def _remote_src(self) -> ta.Sequence[str]:
151
+ return [
152
+ self._payload_src(),
153
+ '_remote_execution_main()',
154
+ ]
155
+
156
+ @cached_nullary
157
+ def _spawn_src(self) -> str:
158
+ return pyremote_build_bootstrap_cmd(__package__ or 'manage')
159
+
160
+ #
161
+
162
+ @contextlib.contextmanager
163
+ def connect(
164
+ self,
165
+ tgt: RemoteSpawning.Target,
166
+ bs: MainBootstrap,
167
+ ) -> ta.Generator[RemoteCommandExecutor, None, None]:
168
+ spawn_src = self._spawn_src()
169
+ remote_src = self._remote_src()
170
+
171
+ with self._spawning.spawn(
172
+ tgt,
173
+ spawn_src,
174
+ ) as proc:
175
+ res = PyremoteBootstrapDriver( # noqa
176
+ remote_src,
177
+ PyremoteBootstrapOptions(
178
+ debug=bs.main_config.debug,
179
+ ),
180
+ ).run(
181
+ proc.stdout,
182
+ proc.stdin,
183
+ )
184
+
185
+ chan = RemoteChannel(
186
+ proc.stdout,
187
+ proc.stdin,
188
+ msh=self._msh,
189
+ )
190
+
191
+ chan.send_obj(bs)
192
+
193
+ yield RemoteCommandExecutor(chan)
@@ -0,0 +1,29 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+ from omlish.lite.inject import InjectorBindingOrBindings
5
+ from omlish.lite.inject import InjectorBindings
6
+ from omlish.lite.inject import inj
7
+
8
+ from .config import RemoteConfig
9
+ from .execution import RemoteExecution
10
+ from .payload import RemoteExecutionPayloadFile
11
+ from .spawning import RemoteSpawning
12
+
13
+
14
+ def bind_remote(
15
+ *,
16
+ remote_config: RemoteConfig,
17
+ ) -> InjectorBindings:
18
+ lst: ta.List[InjectorBindingOrBindings] = [
19
+ inj.bind(remote_config),
20
+
21
+ inj.bind(RemoteSpawning, singleton=True),
22
+
23
+ inj.bind(RemoteExecution, singleton=True),
24
+ ]
25
+
26
+ if (pf := remote_config.payload_file) is not None:
27
+ lst.append(inj.bind(pf, to_key=RemoteExecutionPayloadFile))
28
+
29
+ return inj.as_bindings(*lst)
@@ -6,6 +6,9 @@ import typing as ta
6
6
  from omlish.lite.cached import cached_nullary
7
7
 
8
8
 
9
+ RemoteExecutionPayloadFile = ta.NewType('RemoteExecutionPayloadFile', str)
10
+
11
+
9
12
  @cached_nullary
10
13
  def _get_self_src() -> str:
11
14
  return inspect.getsource(sys.modules[__name__])
@@ -23,7 +26,10 @@ def _is_self_amalg() -> bool:
23
26
  return _is_src_amalg(_get_self_src())
24
27
 
25
28
 
26
- def get_payload_src(*, file: ta.Optional[str]) -> str:
29
+ def get_remote_payload_src(
30
+ *,
31
+ file: ta.Optional[RemoteExecutionPayloadFile],
32
+ ) -> str:
27
33
  if file is not None:
28
34
  with open(file) as f:
29
35
  return f.read()
@@ -6,28 +6,21 @@ import subprocess
6
6
  import typing as ta
7
7
 
8
8
  from omlish.lite.check import check_not_none
9
+ from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
10
+ from omlish.lite.subprocesses import SubprocessChannelOption
9
11
  from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
10
12
 
11
13
 
12
- class PySpawner:
13
- DEFAULT_PYTHON = 'python3'
14
+ class RemoteSpawning:
15
+ @dc.dataclass(frozen=True)
16
+ class Target:
17
+ shell: ta.Optional[str] = None
18
+ shell_quote: bool = False
14
19
 
15
- def __init__(
16
- self,
17
- src: str,
18
- *,
19
- shell: ta.Optional[str] = None,
20
- shell_quote: bool = False,
21
- python: str = DEFAULT_PYTHON,
22
- stderr: ta.Optional[ta.Literal['pipe', 'stdout', 'devnull']] = None,
23
- ) -> None:
24
- super().__init__()
25
-
26
- self._src = src
27
- self._shell = shell
28
- self._shell_quote = shell_quote
29
- self._python = python
30
- self._stderr = stderr
20
+ DEFAULT_PYTHON: ta.ClassVar[str] = 'python3'
21
+ python: str = DEFAULT_PYTHON
22
+
23
+ stderr: ta.Optional[str] = None # SubprocessChannelOption
31
24
 
32
25
  #
33
26
 
@@ -35,31 +28,29 @@ class PySpawner:
35
28
  cmd: ta.Sequence[str]
36
29
  shell: bool
37
30
 
38
- def _prepare_cmd(self) -> _PreparedCmd:
39
- if self._shell is not None:
40
- sh_src = f'{self._python} -c {shlex.quote(self._src)}'
41
- if self._shell_quote:
31
+ def _prepare_cmd(
32
+ self,
33
+ tgt: Target,
34
+ src: str,
35
+ ) -> _PreparedCmd:
36
+ if tgt.shell is not None:
37
+ sh_src = f'{tgt.python} -c {shlex.quote(src)}'
38
+ if tgt.shell_quote:
42
39
  sh_src = shlex.quote(sh_src)
43
- sh_cmd = f'{self._shell} {sh_src}'
44
- return PySpawner._PreparedCmd(
40
+ sh_cmd = f'{tgt.shell} {sh_src}'
41
+ return RemoteSpawning._PreparedCmd(
45
42
  cmd=[sh_cmd],
46
43
  shell=True,
47
44
  )
48
45
 
49
46
  else:
50
- return PySpawner._PreparedCmd(
51
- cmd=[self._python, '-c', self._src],
47
+ return RemoteSpawning._PreparedCmd(
48
+ cmd=[tgt.python, '-c', src],
52
49
  shell=False,
53
50
  )
54
51
 
55
52
  #
56
53
 
57
- _STDERR_KWARG_MAP: ta.Mapping[str, int] = {
58
- 'pipe': subprocess.PIPE,
59
- 'stdout': subprocess.STDOUT,
60
- 'devnull': subprocess.DEVNULL,
61
- }
62
-
63
54
  @dc.dataclass(frozen=True)
64
55
  class Spawned:
65
56
  stdin: ta.IO
@@ -69,23 +60,28 @@ class PySpawner:
69
60
  @contextlib.contextmanager
70
61
  def spawn(
71
62
  self,
63
+ tgt: Target,
64
+ src: str,
72
65
  *,
73
66
  timeout: ta.Optional[float] = None,
74
67
  ) -> ta.Generator[Spawned, None, None]:
75
- pc = self._prepare_cmd()
68
+ pc = self._prepare_cmd(tgt, src)
76
69
 
77
70
  with subprocess.Popen(
78
71
  subprocess_maybe_shell_wrap_exec(*pc.cmd),
79
72
  shell=pc.shell,
80
73
  stdin=subprocess.PIPE,
81
74
  stdout=subprocess.PIPE,
82
- stderr=self._STDERR_KWARG_MAP[self._stderr] if self._stderr is not None else None,
75
+ stderr=(
76
+ SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, tgt.stderr)]
77
+ if tgt.stderr is not None else None
78
+ ),
83
79
  ) as proc:
84
80
  stdin = check_not_none(proc.stdin)
85
81
  stdout = check_not_none(proc.stdout)
86
82
 
87
83
  try:
88
- yield PySpawner.Spawned(
84
+ yield RemoteSpawning.Spawned(
89
85
  stdin=stdin,
90
86
  stdout=stdout,
91
87
  stderr=proc.stderr,