ominfra 0.0.0.dev141__py3-none-any.whl → 0.0.0.dev143__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,