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

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,22 +4,24 @@ import dataclasses as dc
4
4
  import typing as ta
5
5
 
6
6
 
7
- CommandInputT = ta.TypeVar('CommandInputT', bound='Command.Input')
7
+ CommandT = ta.TypeVar('CommandT', bound='Command')
8
8
  CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
9
9
 
10
10
 
11
11
  ##
12
12
 
13
13
 
14
- class Command(abc.ABC, ta.Generic[CommandInputT, CommandOutputT]):
15
- @dc.dataclass(frozen=True)
16
- class Input(abc.ABC): # noqa
17
- pass
18
-
14
+ @dc.dataclass(frozen=True)
15
+ class Command(abc.ABC, ta.Generic[CommandOutputT]):
19
16
  @dc.dataclass(frozen=True)
20
17
  class Output(abc.ABC): # noqa
21
18
  pass
22
19
 
20
+
21
+ ##
22
+
23
+
24
+ class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
23
25
  @abc.abstractmethod
24
- def _execute(self, inp: CommandInputT) -> CommandOutputT:
26
+ def execute(self, i: CommandT) -> CommandOutputT:
25
27
  raise NotImplementedError
@@ -8,29 +8,29 @@ import typing as ta
8
8
  from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
9
9
 
10
10
  from .base import Command
11
+ from .base import CommandExecutor
11
12
 
12
13
 
13
14
  ##
14
15
 
15
16
 
16
- class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Output']):
17
- @dc.dataclass(frozen=True)
18
- class Input(Command.Input):
19
- args: ta.Sequence[str]
17
+ @dc.dataclass(frozen=True)
18
+ class SubprocessCommand(Command['SubprocessCommand.Output']):
19
+ args: ta.Sequence[str]
20
20
 
21
- shell: bool = False
22
- cwd: ta.Optional[str] = None
23
- env: ta.Optional[ta.Mapping[str, str]] = None
21
+ shell: bool = False
22
+ cwd: ta.Optional[str] = None
23
+ env: ta.Optional[ta.Mapping[str, str]] = None
24
24
 
25
- capture_stdout: bool = False
26
- capture_stderr: bool = False
25
+ capture_stdout: bool = False
26
+ capture_stderr: bool = False
27
27
 
28
- input: ta.Optional[bytes] = None
29
- timeout: ta.Optional[float] = None
28
+ input: ta.Optional[bytes] = None
29
+ timeout: ta.Optional[float] = None
30
30
 
31
- def __post_init__(self) -> None:
32
- if isinstance(self.args, str):
33
- raise TypeError(self.args)
31
+ def __post_init__(self) -> None:
32
+ if isinstance(self.args, str):
33
+ raise TypeError(self.args)
34
34
 
35
35
  @dc.dataclass(frozen=True)
36
36
  class Output(Command.Output):
@@ -42,7 +42,12 @@ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Ou
42
42
  stdout: ta.Optional[bytes] = None
43
43
  stderr: ta.Optional[bytes] = None
44
44
 
45
- def _execute(self, inp: Input) -> Output:
45
+
46
+ ##
47
+
48
+
49
+ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
50
+ def execute(self, inp: SubprocessCommand) -> SubprocessCommand.Output:
46
51
  proc = subprocess.Popen(
47
52
  subprocess_maybe_shell_wrap_exec(*inp.args),
48
53
 
ominfra/manage/main.py CHANGED
@@ -3,25 +3,19 @@
3
3
  # ruff: noqa: UP006 UP007
4
4
  """
5
5
  manage.py -s 'docker run -i python:3.12'
6
- manage.py -qs 'ssh -i foo/bar foo@bar.baz' --python=python3.8
6
+ manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
7
7
  """
8
- import inspect
9
8
  import json
10
- import shlex
11
9
  import struct
12
- import subprocess
13
- import sys
14
10
  import typing as ta
15
11
 
16
- from omlish.lite.cached import cached_nullary
17
- from omlish.lite.check import check_not_none
12
+ from omlish.lite.cached import static_init
18
13
  from omlish.lite.json import json_dumps_compact
19
14
  from omlish.lite.marshal import PolymorphicObjMarshaler
20
15
  from omlish.lite.marshal import get_obj_marshaler
21
16
  from omlish.lite.marshal import marshal_obj
22
17
  from omlish.lite.marshal import register_opj_marshaler
23
18
  from omlish.lite.marshal import unmarshal_obj
24
- from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
25
19
 
26
20
  from ..pyremote import PyremoteBootstrapDriver
27
21
  from ..pyremote import PyremoteBootstrapOptions
@@ -29,6 +23,9 @@ from ..pyremote import pyremote_bootstrap_finalize
29
23
  from ..pyremote import pyremote_build_bootstrap_cmd
30
24
  from .commands.base import Command
31
25
  from .commands.subprocess import SubprocessCommand
26
+ from .commands.subprocess import SubprocessCommandExecutor
27
+ from .payload import get_payload_src
28
+ from .spawning import PySpawner
32
29
 
33
30
 
34
31
  ##
@@ -39,29 +36,23 @@ _COMMAND_TYPES = {
39
36
  }
40
37
 
41
38
 
42
- register_opj_marshaler(
43
- Command.Input,
44
- PolymorphicObjMarshaler.of([
45
- PolymorphicObjMarshaler.Impl(
46
- cty.Input,
47
- k,
48
- get_obj_marshaler(cty.Input),
49
- )
50
- for k, cty in _COMMAND_TYPES.items()
51
- ]),
52
- )
53
-
54
- register_opj_marshaler(
55
- Command.Output,
56
- PolymorphicObjMarshaler.of([
57
- PolymorphicObjMarshaler.Impl(
58
- cty.Output,
59
- k,
60
- get_obj_marshaler(cty.Output),
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
+ ]),
61
55
  )
62
- for k, cty in _COMMAND_TYPES.items()
63
- ]),
64
- )
65
56
 
66
57
 
67
58
  ##
@@ -99,12 +90,12 @@ def _remote_main() -> None:
99
90
  rt = pyremote_bootstrap_finalize() # noqa
100
91
 
101
92
  while True:
102
- i = _recv_obj(rt.input, Command.Input)
93
+ i = _recv_obj(rt.input, Command)
103
94
  if i is None:
104
95
  break
105
96
 
106
- if isinstance(i, SubprocessCommand.Input):
107
- o = SubprocessCommand()._execute(i) # noqa
97
+ if isinstance(i, SubprocessCommand):
98
+ o = SubprocessCommandExecutor().execute(i) # noqa
108
99
  else:
109
100
  raise TypeError(i)
110
101
 
@@ -114,38 +105,6 @@ def _remote_main() -> None:
114
105
  ##
115
106
 
116
107
 
117
- @cached_nullary
118
- def _get_self_src() -> str:
119
- return inspect.getsource(sys.modules[__name__])
120
-
121
-
122
- def _is_src_amalg(src: str) -> bool:
123
- for l in src.splitlines(): # noqa
124
- if l.startswith('# @omlish-amalg-output '):
125
- return True
126
- return False
127
-
128
-
129
- @cached_nullary
130
- def _is_self_amalg() -> bool:
131
- return _is_src_amalg(_get_self_src())
132
-
133
-
134
- def _get_amalg_src(*, amalg_file: ta.Optional[str]) -> str:
135
- if amalg_file is not None:
136
- with open(amalg_file) as f:
137
- return f.read()
138
-
139
- if _is_self_amalg():
140
- return _get_self_src()
141
-
142
- import importlib.resources
143
- return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
144
-
145
-
146
- ##
147
-
148
-
149
108
  def _main() -> None:
150
109
  import argparse
151
110
 
@@ -154,19 +113,20 @@ def _main() -> None:
154
113
  parser.add_argument('-s', '--shell')
155
114
  parser.add_argument('-q', '--shell-quote', action='store_true')
156
115
  parser.add_argument('--python', default='python3')
157
- parser.add_argument('--_amalg-file')
116
+ parser.add_argument('--debug', action='store_true')
117
+ parser.add_argument('--_payload-file')
158
118
 
159
119
  args = parser.parse_args()
160
120
 
161
121
  #
162
122
 
163
- amalg_src = _get_amalg_src(amalg_file=args._amalg_file) # noqa
123
+ payload_src = get_payload_src(file=args._payload_file) # noqa
164
124
 
165
125
  #
166
126
 
167
127
  remote_src = '\n\n'.join([
168
128
  '__name__ = "__remote__"',
169
- amalg_src,
129
+ payload_src,
170
130
  '_remote_main()',
171
131
  ])
172
132
 
@@ -174,60 +134,41 @@ def _main() -> None:
174
134
 
175
135
  bs_src = pyremote_build_bootstrap_cmd(__package__ or 'manage')
176
136
 
177
- if args.shell is not None:
178
- sh_src = f'{args.python} -c {shlex.quote(bs_src)}'
179
- if args.shell_quote:
180
- sh_src = shlex.quote(sh_src)
181
- sh_cmd = f'{args.shell} {sh_src}'
182
- cmd = [sh_cmd]
183
- shell = True
184
- else:
185
- cmd = [args.python, '-c', bs_src]
186
- shell = False
187
-
188
- proc = subprocess.Popen(
189
- subprocess_maybe_shell_wrap_exec(*cmd),
190
- shell=shell,
191
- stdin=subprocess.PIPE,
192
- stdout=subprocess.PIPE,
193
- )
194
-
195
- stdin = check_not_none(proc.stdin)
196
- stdout = check_not_none(proc.stdout)
197
-
198
- res = PyremoteBootstrapDriver( # noqa
199
- remote_src,
200
- PyremoteBootstrapOptions(
201
- # debug=True,
202
- ),
203
- ).run(stdin, stdout)
204
- # print(res)
205
-
206
137
  #
207
138
 
208
- for ci in [
209
- SubprocessCommand.Input(
210
- args=['python3', '-'],
211
- input=b'print(1)\n',
212
- capture_stdout=True,
213
- ),
214
- SubprocessCommand.Input(
215
- args=['uname'],
216
- capture_stdout=True,
217
- ),
218
- ]:
219
- _send_obj(stdin, ci, Command.Input)
220
-
221
- o = _recv_obj(stdout, Command.Output)
222
-
223
- print(o)
224
-
225
- try:
226
- stdin.close()
227
- except BrokenPipeError:
228
- pass
229
-
230
- proc.wait()
139
+ spawner = PySpawner(
140
+ bs_src,
141
+ shell=args.shell,
142
+ shell_quote=args.shell_quote,
143
+ python=args.python,
144
+ )
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)
231
172
 
232
173
 
233
174
  if __name__ == '__main__':
@@ -0,0 +1,35 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import inspect
3
+ import sys
4
+ import typing as ta
5
+
6
+ from omlish.lite.cached import cached_nullary
7
+
8
+
9
+ @cached_nullary
10
+ def _get_self_src() -> str:
11
+ return inspect.getsource(sys.modules[__name__])
12
+
13
+
14
+ def _is_src_amalg(src: str) -> bool:
15
+ for l in src.splitlines(): # noqa
16
+ if l.startswith('# @omlish-amalg-output '):
17
+ return True
18
+ return False
19
+
20
+
21
+ @cached_nullary
22
+ def _is_self_amalg() -> bool:
23
+ return _is_src_amalg(_get_self_src())
24
+
25
+
26
+ def get_payload_src(*, file: ta.Optional[str]) -> str:
27
+ if file is not None:
28
+ with open(file) as f:
29
+ return f.read()
30
+
31
+ if _is_self_amalg():
32
+ return _get_self_src()
33
+
34
+ import importlib.resources
35
+ return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
@@ -0,0 +1,100 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import contextlib
3
+ import dataclasses as dc
4
+ import shlex
5
+ import subprocess
6
+ import typing as ta
7
+
8
+ from omlish.lite.check import check_not_none
9
+ from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
10
+
11
+
12
+ class PySpawner:
13
+ DEFAULT_PYTHON = 'python3'
14
+
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
31
+
32
+ #
33
+
34
+ class _PreparedCmd(ta.NamedTuple):
35
+ cmd: ta.Sequence[str]
36
+ shell: bool
37
+
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:
42
+ sh_src = shlex.quote(sh_src)
43
+ sh_cmd = f'{self._shell} {sh_src}'
44
+ return PySpawner._PreparedCmd(
45
+ cmd=[sh_cmd],
46
+ shell=True,
47
+ )
48
+
49
+ else:
50
+ return PySpawner._PreparedCmd(
51
+ cmd=[self._python, '-c', self._src],
52
+ shell=False,
53
+ )
54
+
55
+ #
56
+
57
+ _STDERR_KWARG_MAP: ta.Mapping[str, int] = {
58
+ 'pipe': subprocess.PIPE,
59
+ 'stdout': subprocess.STDOUT,
60
+ 'devnull': subprocess.DEVNULL,
61
+ }
62
+
63
+ @dc.dataclass(frozen=True)
64
+ class Spawned:
65
+ stdin: ta.IO
66
+ stdout: ta.IO
67
+ stderr: ta.Optional[ta.IO]
68
+
69
+ @contextlib.contextmanager
70
+ def spawn(
71
+ self,
72
+ *,
73
+ timeout: ta.Optional[float] = None,
74
+ ) -> ta.Generator[Spawned, None, None]:
75
+ pc = self._prepare_cmd()
76
+
77
+ with subprocess.Popen(
78
+ subprocess_maybe_shell_wrap_exec(*pc.cmd),
79
+ shell=pc.shell,
80
+ stdin=subprocess.PIPE,
81
+ stdout=subprocess.PIPE,
82
+ stderr=self._STDERR_KWARG_MAP[self._stderr] if self._stderr is not None else None,
83
+ ) as proc:
84
+ stdin = check_not_none(proc.stdin)
85
+ stdout = check_not_none(proc.stdout)
86
+
87
+ try:
88
+ yield PySpawner.Spawned(
89
+ stdin=stdin,
90
+ stdout=stdout,
91
+ stderr=proc.stderr,
92
+ )
93
+
94
+ finally:
95
+ try:
96
+ stdin.close()
97
+ except BrokenPipeError:
98
+ pass
99
+
100
+ proc.wait(timeout)
ominfra/pyremote.py CHANGED
@@ -132,6 +132,8 @@ _PYREMOTE_BOOTSTRAP_SRC_FD = 101
132
132
 
133
133
  _PYREMOTE_BOOTSTRAP_CHILD_PID_VAR = '_OPYR_CHILD_PID'
134
134
  _PYREMOTE_BOOTSTRAP_ARGV0_VAR = '_OPYR_ARGV0'
135
+ _PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR = '_OPYR_CONTEXT_NAME'
136
+ _PYREMOTE_BOOTSTRAP_SRC_FILE_VAR = '_OPYR_SRC_FILE'
135
137
  _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR = '_OPYR_OPTIONS_JSON'
136
138
 
137
139
  _PYREMOTE_BOOTSTRAP_ACK0 = b'OPYR000\n'
@@ -174,11 +176,10 @@ def _pyremote_bootstrap_main(context_name: str) -> None:
174
176
  for f in [r0, w0, r1, w1]:
175
177
  os.close(f)
176
178
 
177
- # Save child pid to close after relaunch
179
+ # Save vars
178
180
  os.environ[_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR] = str(cp)
179
-
180
- # Save original argv0
181
181
  os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR] = sys.executable
182
+ os.environ[_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR] = context_name
182
183
 
183
184
  # Start repl reading stdin from r0
184
185
  os.execl(sys.executable, sys.executable + (_PYREMOTE_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)))
@@ -229,6 +230,7 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
229
230
 
230
231
  '_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR',
231
232
  '_PYREMOTE_BOOTSTRAP_ARGV0_VAR',
233
+ '_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR',
232
234
 
233
235
  '_PYREMOTE_BOOTSTRAP_ACK0',
234
236
  '_PYREMOTE_BOOTSTRAP_ACK1',
@@ -264,14 +266,15 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
264
266
  class PyremotePayloadRuntime:
265
267
  input: ta.BinaryIO
266
268
  output: ta.BinaryIO
269
+ context_name: str
267
270
  main_src: str
268
271
  options: PyremoteBootstrapOptions
269
272
  env_info: PyremoteEnvInfo
270
273
 
271
274
 
272
275
  def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
273
- # If json options var is not present we need to do initial finalization
274
- if _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR not in os.environ:
276
+ # If src file var is not present we need to do initial finalization
277
+ if _PYREMOTE_BOOTSTRAP_SRC_FILE_VAR not in os.environ:
275
278
  # Read second copy of main src
276
279
  r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
277
280
  main_src = r1.read().decode('utf-8')
@@ -295,11 +298,14 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
295
298
  os.write(tfd, main_src.encode('utf-8'))
296
299
  os.close(tfd)
297
300
 
298
- # Set json options var
301
+ # Set vars
302
+ os.environ[_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR] = tfn
299
303
  os.environ[_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR] = options_json.decode('utf-8')
300
304
 
301
305
  # Re-exec temp file
302
- os.execl(os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR], sys.orig_argv[0], tfn)
306
+ exe = os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR]
307
+ context_name = os.environ[_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR]
308
+ os.execl(exe, exe + (_PYREMOTE_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)), tfn)
303
309
 
304
310
  else:
305
311
  # Load options json var
@@ -307,12 +313,15 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
307
313
  options = PyremoteBootstrapOptions(**json.loads(options_json_str))
308
314
 
309
315
  # Read temp source file
310
- with open(sys.orig_argv[1]) as sf:
316
+ with open(os.environ.pop(_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR)) as sf:
311
317
  main_src = sf.read()
312
318
 
313
319
  # Restore original argv0
314
320
  sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
315
321
 
322
+ # Grab context name
323
+ context_name = os.environ.pop(_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR)
324
+
316
325
  # Write third ack
317
326
  os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
318
327
 
@@ -335,6 +344,7 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
335
344
  return PyremotePayloadRuntime(
336
345
  input=input,
337
346
  output=output,
347
+ context_name=context_name,
338
348
  main_src=main_src,
339
349
  options=options,
340
350
  env_info=env_info,
@@ -58,6 +58,7 @@ TomlPos = int # ta.TypeAlias
58
58
 
59
59
  # ../../../../omlish/lite/cached.py
60
60
  T = ta.TypeVar('T')
61
+ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
61
62
 
62
63
  # ../../../../omlish/lite/check.py
63
64
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
@@ -915,6 +916,12 @@ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
915
916
  return _cached_nullary(fn)
916
917
 
917
918
 
919
+ def static_init(fn: CallableT) -> CallableT:
920
+ fn = cached_nullary(fn)
921
+ fn()
922
+ return fn
923
+
924
+
918
925
  ########################################
919
926
  # ../../../../../omlish/lite/check.py
920
927
 
ominfra/scripts/manage.py CHANGED
@@ -6,7 +6,7 @@
6
6
  # ruff: noqa: N802 UP006 UP007 UP036
7
7
  """
8
8
  manage.py -s 'docker run -i python:3.12'
9
- manage.py -qs 'ssh -i foo/bar foo@bar.baz' --python=python3.8
9
+ manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
10
10
  """
11
11
  import abc
12
12
  import base64
@@ -49,11 +49,12 @@ if sys.version_info < (3, 8):
49
49
 
50
50
 
51
51
  # commands/base.py
52
- CommandInputT = ta.TypeVar('CommandInputT', bound='Command.Input')
52
+ CommandT = ta.TypeVar('CommandT', bound='Command')
53
53
  CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
54
54
 
55
55
  # ../../omlish/lite/cached.py
56
56
  T = ta.TypeVar('T')
57
+ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
57
58
 
58
59
  # ../../omlish/lite/check.py
59
60
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
@@ -66,17 +67,19 @@ SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
66
67
  ##
67
68
 
68
69
 
69
- class Command(abc.ABC, ta.Generic[CommandInputT, CommandOutputT]):
70
- @dc.dataclass(frozen=True)
71
- class Input(abc.ABC): # noqa
72
- pass
73
-
70
+ @dc.dataclass(frozen=True)
71
+ class Command(abc.ABC, ta.Generic[CommandOutputT]):
74
72
  @dc.dataclass(frozen=True)
75
73
  class Output(abc.ABC): # noqa
76
74
  pass
77
75
 
76
+
77
+ ##
78
+
79
+
80
+ class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
78
81
  @abc.abstractmethod
79
- def _execute(self, inp: CommandInputT) -> CommandOutputT:
82
+ def execute(self, i: CommandT) -> CommandOutputT:
80
83
  raise NotImplementedError
81
84
 
82
85
 
@@ -203,6 +206,8 @@ _PYREMOTE_BOOTSTRAP_SRC_FD = 101
203
206
 
204
207
  _PYREMOTE_BOOTSTRAP_CHILD_PID_VAR = '_OPYR_CHILD_PID'
205
208
  _PYREMOTE_BOOTSTRAP_ARGV0_VAR = '_OPYR_ARGV0'
209
+ _PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR = '_OPYR_CONTEXT_NAME'
210
+ _PYREMOTE_BOOTSTRAP_SRC_FILE_VAR = '_OPYR_SRC_FILE'
206
211
  _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR = '_OPYR_OPTIONS_JSON'
207
212
 
208
213
  _PYREMOTE_BOOTSTRAP_ACK0 = b'OPYR000\n'
@@ -245,11 +250,10 @@ def _pyremote_bootstrap_main(context_name: str) -> None:
245
250
  for f in [r0, w0, r1, w1]:
246
251
  os.close(f)
247
252
 
248
- # Save child pid to close after relaunch
253
+ # Save vars
249
254
  os.environ[_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR] = str(cp)
250
-
251
- # Save original argv0
252
255
  os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR] = sys.executable
256
+ os.environ[_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR] = context_name
253
257
 
254
258
  # Start repl reading stdin from r0
255
259
  os.execl(sys.executable, sys.executable + (_PYREMOTE_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)))
@@ -300,6 +304,7 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
300
304
 
301
305
  '_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR',
302
306
  '_PYREMOTE_BOOTSTRAP_ARGV0_VAR',
307
+ '_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR',
303
308
 
304
309
  '_PYREMOTE_BOOTSTRAP_ACK0',
305
310
  '_PYREMOTE_BOOTSTRAP_ACK1',
@@ -335,14 +340,15 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
335
340
  class PyremotePayloadRuntime:
336
341
  input: ta.BinaryIO
337
342
  output: ta.BinaryIO
343
+ context_name: str
338
344
  main_src: str
339
345
  options: PyremoteBootstrapOptions
340
346
  env_info: PyremoteEnvInfo
341
347
 
342
348
 
343
349
  def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
344
- # If json options var is not present we need to do initial finalization
345
- if _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR not in os.environ:
350
+ # If src file var is not present we need to do initial finalization
351
+ if _PYREMOTE_BOOTSTRAP_SRC_FILE_VAR not in os.environ:
346
352
  # Read second copy of main src
347
353
  r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
348
354
  main_src = r1.read().decode('utf-8')
@@ -366,11 +372,14 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
366
372
  os.write(tfd, main_src.encode('utf-8'))
367
373
  os.close(tfd)
368
374
 
369
- # Set json options var
375
+ # Set vars
376
+ os.environ[_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR] = tfn
370
377
  os.environ[_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR] = options_json.decode('utf-8')
371
378
 
372
379
  # Re-exec temp file
373
- os.execl(os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR], sys.orig_argv[0], tfn)
380
+ exe = os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR]
381
+ context_name = os.environ[_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR]
382
+ os.execl(exe, exe + (_PYREMOTE_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)), tfn)
374
383
 
375
384
  else:
376
385
  # Load options json var
@@ -378,12 +387,15 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
378
387
  options = PyremoteBootstrapOptions(**json.loads(options_json_str))
379
388
 
380
389
  # Read temp source file
381
- with open(sys.orig_argv[1]) as sf:
390
+ with open(os.environ.pop(_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR)) as sf:
382
391
  main_src = sf.read()
383
392
 
384
393
  # Restore original argv0
385
394
  sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
386
395
 
396
+ # Grab context name
397
+ context_name = os.environ.pop(_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR)
398
+
387
399
  # Write third ack
388
400
  os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
389
401
 
@@ -406,6 +418,7 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
406
418
  return PyremotePayloadRuntime(
407
419
  input=input,
408
420
  output=output,
421
+ context_name=context_name,
409
422
  main_src=main_src,
410
423
  options=options,
411
424
  env_info=env_info,
@@ -550,6 +563,12 @@ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
550
563
  return _cached_nullary(fn)
551
564
 
552
565
 
566
+ def static_init(fn: CallableT) -> CallableT:
567
+ fn = cached_nullary(fn)
568
+ fn()
569
+ return fn
570
+
571
+
553
572
  ########################################
554
573
  # ../../../omlish/lite/check.py
555
574
 
@@ -733,6 +752,39 @@ def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
733
752
  todo.extend(reversed(cur.__subclasses__()))
734
753
 
735
754
 
755
+ ########################################
756
+ # ../payload.py
757
+
758
+
759
+ @cached_nullary
760
+ def _get_self_src() -> str:
761
+ return inspect.getsource(sys.modules[__name__])
762
+
763
+
764
+ def _is_src_amalg(src: str) -> bool:
765
+ for l in src.splitlines(): # noqa
766
+ if l.startswith('# @omlish-amalg-output '):
767
+ return True
768
+ return False
769
+
770
+
771
+ @cached_nullary
772
+ def _is_self_amalg() -> bool:
773
+ return _is_src_amalg(_get_self_src())
774
+
775
+
776
+ def get_payload_src(*, file: ta.Optional[str]) -> str:
777
+ if file is not None:
778
+ with open(file) as f:
779
+ return f.read()
780
+
781
+ if _is_self_amalg():
782
+ return _get_self_src()
783
+
784
+ import importlib.resources
785
+ return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
786
+
787
+
736
788
  ########################################
737
789
  # ../../../omlish/lite/logs.py
738
790
  """
@@ -1498,24 +1550,23 @@ def subprocess_close(
1498
1550
  ##
1499
1551
 
1500
1552
 
1501
- class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Output']):
1502
- @dc.dataclass(frozen=True)
1503
- class Input(Command.Input):
1504
- args: ta.Sequence[str]
1553
+ @dc.dataclass(frozen=True)
1554
+ class SubprocessCommand(Command['SubprocessCommand.Output']):
1555
+ args: ta.Sequence[str]
1505
1556
 
1506
- shell: bool = False
1507
- cwd: ta.Optional[str] = None
1508
- env: ta.Optional[ta.Mapping[str, str]] = None
1557
+ shell: bool = False
1558
+ cwd: ta.Optional[str] = None
1559
+ env: ta.Optional[ta.Mapping[str, str]] = None
1509
1560
 
1510
- capture_stdout: bool = False
1511
- capture_stderr: bool = False
1561
+ capture_stdout: bool = False
1562
+ capture_stderr: bool = False
1512
1563
 
1513
- input: ta.Optional[bytes] = None
1514
- timeout: ta.Optional[float] = None
1564
+ input: ta.Optional[bytes] = None
1565
+ timeout: ta.Optional[float] = None
1515
1566
 
1516
- def __post_init__(self) -> None:
1517
- if isinstance(self.args, str):
1518
- raise TypeError(self.args)
1567
+ def __post_init__(self) -> None:
1568
+ if isinstance(self.args, str):
1569
+ raise TypeError(self.args)
1519
1570
 
1520
1571
  @dc.dataclass(frozen=True)
1521
1572
  class Output(Command.Output):
@@ -1527,7 +1578,12 @@ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Ou
1527
1578
  stdout: ta.Optional[bytes] = None
1528
1579
  stderr: ta.Optional[bytes] = None
1529
1580
 
1530
- def _execute(self, inp: Input) -> Output:
1581
+
1582
+ ##
1583
+
1584
+
1585
+ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
1586
+ def execute(self, inp: SubprocessCommand) -> SubprocessCommand.Output:
1531
1587
  proc = subprocess.Popen(
1532
1588
  subprocess_maybe_shell_wrap_exec(*inp.args),
1533
1589
 
@@ -1558,6 +1614,101 @@ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Ou
1558
1614
  )
1559
1615
 
1560
1616
 
1617
+ ########################################
1618
+ # ../spawning.py
1619
+
1620
+
1621
+ class PySpawner:
1622
+ DEFAULT_PYTHON = 'python3'
1623
+
1624
+ def __init__(
1625
+ self,
1626
+ src: str,
1627
+ *,
1628
+ shell: ta.Optional[str] = None,
1629
+ shell_quote: bool = False,
1630
+ python: str = DEFAULT_PYTHON,
1631
+ stderr: ta.Optional[ta.Literal['pipe', 'stdout', 'devnull']] = None,
1632
+ ) -> None:
1633
+ super().__init__()
1634
+
1635
+ self._src = src
1636
+ self._shell = shell
1637
+ self._shell_quote = shell_quote
1638
+ self._python = python
1639
+ self._stderr = stderr
1640
+
1641
+ #
1642
+
1643
+ class _PreparedCmd(ta.NamedTuple):
1644
+ cmd: ta.Sequence[str]
1645
+ shell: bool
1646
+
1647
+ def _prepare_cmd(self) -> _PreparedCmd:
1648
+ if self._shell is not None:
1649
+ sh_src = f'{self._python} -c {shlex.quote(self._src)}'
1650
+ if self._shell_quote:
1651
+ sh_src = shlex.quote(sh_src)
1652
+ sh_cmd = f'{self._shell} {sh_src}'
1653
+ return PySpawner._PreparedCmd(
1654
+ cmd=[sh_cmd],
1655
+ shell=True,
1656
+ )
1657
+
1658
+ else:
1659
+ return PySpawner._PreparedCmd(
1660
+ cmd=[self._python, '-c', self._src],
1661
+ shell=False,
1662
+ )
1663
+
1664
+ #
1665
+
1666
+ _STDERR_KWARG_MAP: ta.Mapping[str, int] = {
1667
+ 'pipe': subprocess.PIPE,
1668
+ 'stdout': subprocess.STDOUT,
1669
+ 'devnull': subprocess.DEVNULL,
1670
+ }
1671
+
1672
+ @dc.dataclass(frozen=True)
1673
+ class Spawned:
1674
+ stdin: ta.IO
1675
+ stdout: ta.IO
1676
+ stderr: ta.Optional[ta.IO]
1677
+
1678
+ @contextlib.contextmanager
1679
+ def spawn(
1680
+ self,
1681
+ *,
1682
+ timeout: ta.Optional[float] = None,
1683
+ ) -> ta.Generator[Spawned, None, None]:
1684
+ pc = self._prepare_cmd()
1685
+
1686
+ with subprocess.Popen(
1687
+ subprocess_maybe_shell_wrap_exec(*pc.cmd),
1688
+ shell=pc.shell,
1689
+ stdin=subprocess.PIPE,
1690
+ stdout=subprocess.PIPE,
1691
+ stderr=self._STDERR_KWARG_MAP[self._stderr] if self._stderr is not None else None,
1692
+ ) as proc:
1693
+ stdin = check_not_none(proc.stdin)
1694
+ stdout = check_not_none(proc.stdout)
1695
+
1696
+ try:
1697
+ yield PySpawner.Spawned(
1698
+ stdin=stdin,
1699
+ stdout=stdout,
1700
+ stderr=proc.stderr,
1701
+ )
1702
+
1703
+ finally:
1704
+ try:
1705
+ stdin.close()
1706
+ except BrokenPipeError:
1707
+ pass
1708
+
1709
+ proc.wait(timeout)
1710
+
1711
+
1561
1712
  ########################################
1562
1713
  # main.py
1563
1714
 
@@ -1570,29 +1721,23 @@ _COMMAND_TYPES = {
1570
1721
  }
1571
1722
 
1572
1723
 
1573
- register_opj_marshaler(
1574
- Command.Input,
1575
- PolymorphicObjMarshaler.of([
1576
- PolymorphicObjMarshaler.Impl(
1577
- cty.Input,
1578
- k,
1579
- get_obj_marshaler(cty.Input),
1580
- )
1581
- for k, cty in _COMMAND_TYPES.items()
1582
- ]),
1583
- )
1584
-
1585
- register_opj_marshaler(
1586
- Command.Output,
1587
- PolymorphicObjMarshaler.of([
1588
- PolymorphicObjMarshaler.Impl(
1589
- cty.Output,
1590
- k,
1591
- get_obj_marshaler(cty.Output),
1724
+ @static_init
1725
+ def _register_command_marshaling() -> None:
1726
+ for fn in [
1727
+ lambda c: c,
1728
+ lambda c: c.Output,
1729
+ ]:
1730
+ register_opj_marshaler(
1731
+ fn(Command),
1732
+ PolymorphicObjMarshaler.of([
1733
+ PolymorphicObjMarshaler.Impl(
1734
+ fn(cty),
1735
+ k,
1736
+ get_obj_marshaler(fn(cty)),
1737
+ )
1738
+ for k, cty in _COMMAND_TYPES.items()
1739
+ ]),
1592
1740
  )
1593
- for k, cty in _COMMAND_TYPES.items()
1594
- ]),
1595
- )
1596
1741
 
1597
1742
 
1598
1743
  ##
@@ -1630,12 +1775,12 @@ def _remote_main() -> None:
1630
1775
  rt = pyremote_bootstrap_finalize() # noqa
1631
1776
 
1632
1777
  while True:
1633
- i = _recv_obj(rt.input, Command.Input)
1778
+ i = _recv_obj(rt.input, Command)
1634
1779
  if i is None:
1635
1780
  break
1636
1781
 
1637
- if isinstance(i, SubprocessCommand.Input):
1638
- o = SubprocessCommand()._execute(i) # noqa
1782
+ if isinstance(i, SubprocessCommand):
1783
+ o = SubprocessCommandExecutor().execute(i) # noqa
1639
1784
  else:
1640
1785
  raise TypeError(i)
1641
1786
 
@@ -1645,38 +1790,6 @@ def _remote_main() -> None:
1645
1790
  ##
1646
1791
 
1647
1792
 
1648
- @cached_nullary
1649
- def _get_self_src() -> str:
1650
- return inspect.getsource(sys.modules[__name__])
1651
-
1652
-
1653
- def _is_src_amalg(src: str) -> bool:
1654
- for l in src.splitlines(): # noqa
1655
- if l.startswith('# @omlish-amalg-output '):
1656
- return True
1657
- return False
1658
-
1659
-
1660
- @cached_nullary
1661
- def _is_self_amalg() -> bool:
1662
- return _is_src_amalg(_get_self_src())
1663
-
1664
-
1665
- def _get_amalg_src(*, amalg_file: ta.Optional[str]) -> str:
1666
- if amalg_file is not None:
1667
- with open(amalg_file) as f:
1668
- return f.read()
1669
-
1670
- if _is_self_amalg():
1671
- return _get_self_src()
1672
-
1673
- import importlib.resources
1674
- return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
1675
-
1676
-
1677
- ##
1678
-
1679
-
1680
1793
  def _main() -> None:
1681
1794
  import argparse
1682
1795
 
@@ -1685,19 +1798,20 @@ def _main() -> None:
1685
1798
  parser.add_argument('-s', '--shell')
1686
1799
  parser.add_argument('-q', '--shell-quote', action='store_true')
1687
1800
  parser.add_argument('--python', default='python3')
1688
- parser.add_argument('--_amalg-file')
1801
+ parser.add_argument('--debug', action='store_true')
1802
+ parser.add_argument('--_payload-file')
1689
1803
 
1690
1804
  args = parser.parse_args()
1691
1805
 
1692
1806
  #
1693
1807
 
1694
- amalg_src = _get_amalg_src(amalg_file=args._amalg_file) # noqa
1808
+ payload_src = get_payload_src(file=args._payload_file) # noqa
1695
1809
 
1696
1810
  #
1697
1811
 
1698
1812
  remote_src = '\n\n'.join([
1699
1813
  '__name__ = "__remote__"',
1700
- amalg_src,
1814
+ payload_src,
1701
1815
  '_remote_main()',
1702
1816
  ])
1703
1817
 
@@ -1705,60 +1819,41 @@ def _main() -> None:
1705
1819
 
1706
1820
  bs_src = pyremote_build_bootstrap_cmd(__package__ or 'manage')
1707
1821
 
1708
- if args.shell is not None:
1709
- sh_src = f'{args.python} -c {shlex.quote(bs_src)}'
1710
- if args.shell_quote:
1711
- sh_src = shlex.quote(sh_src)
1712
- sh_cmd = f'{args.shell} {sh_src}'
1713
- cmd = [sh_cmd]
1714
- shell = True
1715
- else:
1716
- cmd = [args.python, '-c', bs_src]
1717
- shell = False
1718
-
1719
- proc = subprocess.Popen(
1720
- subprocess_maybe_shell_wrap_exec(*cmd),
1721
- shell=shell,
1722
- stdin=subprocess.PIPE,
1723
- stdout=subprocess.PIPE,
1724
- )
1725
-
1726
- stdin = check_not_none(proc.stdin)
1727
- stdout = check_not_none(proc.stdout)
1728
-
1729
- res = PyremoteBootstrapDriver( # noqa
1730
- remote_src,
1731
- PyremoteBootstrapOptions(
1732
- # debug=True,
1733
- ),
1734
- ).run(stdin, stdout)
1735
- # print(res)
1736
-
1737
1822
  #
1738
1823
 
1739
- for ci in [
1740
- SubprocessCommand.Input(
1741
- args=['python3', '-'],
1742
- input=b'print(1)\n',
1743
- capture_stdout=True,
1744
- ),
1745
- SubprocessCommand.Input(
1746
- args=['uname'],
1747
- capture_stdout=True,
1748
- ),
1749
- ]:
1750
- _send_obj(stdin, ci, Command.Input)
1751
-
1752
- o = _recv_obj(stdout, Command.Output)
1753
-
1754
- print(o)
1824
+ spawner = PySpawner(
1825
+ bs_src,
1826
+ shell=args.shell,
1827
+ shell_quote=args.shell_quote,
1828
+ python=args.python,
1829
+ )
1830
+ with spawner.spawn() as proc:
1831
+ res = PyremoteBootstrapDriver( # noqa
1832
+ remote_src,
1833
+ PyremoteBootstrapOptions(
1834
+ debug=args.debug,
1835
+ ),
1836
+ ).run(proc.stdin, proc.stdout)
1837
+ # print(res)
1755
1838
 
1756
- try:
1757
- stdin.close()
1758
- except BrokenPipeError:
1759
- pass
1839
+ #
1760
1840
 
1761
- proc.wait()
1841
+ for ci in [
1842
+ SubprocessCommand(
1843
+ args=['python3', '-'],
1844
+ input=b'print(1)\n',
1845
+ capture_stdout=True,
1846
+ ),
1847
+ SubprocessCommand(
1848
+ args=['uname'],
1849
+ capture_stdout=True,
1850
+ ),
1851
+ ]:
1852
+ _send_obj(proc.stdin, ci, Command)
1853
+
1854
+ o = _recv_obj(proc.stdout, Command.Output)
1855
+
1856
+ print(o)
1762
1857
 
1763
1858
 
1764
1859
  if __name__ == '__main__':
@@ -101,6 +101,7 @@ V = ta.TypeVar('V')
101
101
 
102
102
  # ../../omlish/lite/cached.py
103
103
  T = ta.TypeVar('T')
104
+ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
104
105
 
105
106
  # ../../omlish/lite/check.py
106
107
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
@@ -1454,6 +1455,12 @@ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
1454
1455
  return _cached_nullary(fn)
1455
1456
 
1456
1457
 
1458
+ def static_init(fn: CallableT) -> CallableT:
1459
+ fn = cached_nullary(fn)
1460
+ fn()
1461
+ return fn
1462
+
1463
+
1457
1464
  ########################################
1458
1465
  # ../../../omlish/lite/check.py
1459
1466
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev139
3
+ Version: 0.0.0.dev141
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev==0.0.0.dev139
16
- Requires-Dist: omlish==0.0.0.dev139
15
+ Requires-Dist: omdev==0.0.0.dev141
16
+ Requires-Dist: omlish==0.0.0.dev141
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -3,7 +3,7 @@ ominfra/__about__.py,sha256=6i1AoruFYQCd-PyhhbDQDWY2d1tiQu9nkwWr-fXAqfY,705
3
3
  ominfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ominfra/cmds.py,sha256=E0AfnvEmnKntXWvmLW5L05_NeDpBET1VBXn7vV6EwBQ,2083
5
5
  ominfra/configs.py,sha256=8aU1Qmbr-qjaE2iP3gAbA2SWJYMPZ-uGK007L01PoOI,1727
6
- ominfra/pyremote.py,sha256=vIEUncFgYvcFj3aK0dvJkRuAuaaRyqXk7c8rl2WPOR4,12599
6
+ ominfra/pyremote.py,sha256=vuLEZSjYtil7Knl4qlXYI4dqqiEGJvuO7EIcqr8Kabk,13142
7
7
  ominfra/ssh.py,sha256=jQpc4WvkMckIfk4vILda8zFaeharRqc_6wxW50b0OjQ,5431
8
8
  ominfra/threadworkers.py,sha256=oX4ubZn7h932saXpRIJu2MNhBExgGGMuGhdXarZxLJw,4948
9
9
  ominfra/clouds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -28,14 +28,16 @@ ominfra/journald/genmessages.py,sha256=rLTS-K2v7otNOtTz4RoOEVYCm0fQuuBzf47e0T61t
28
28
  ominfra/journald/messages.py,sha256=Wr7TjWMOySc0WnKwp_-idR8RfeKlbyJQ_KkELr0VB70,2192
29
29
  ominfra/journald/tailer.py,sha256=5abcFMfgi7fnY9ZEQe2ZVobaJxjQkeu6d9Kagw33a1w,33525
30
30
  ominfra/manage/__init__.py,sha256=YqHcYMfmo04OoVNYc6Ykc1I8WaJot44skwhm9DcnmXE,166
31
- ominfra/manage/main.py,sha256=D-wxBsGi-EBBpKBC1hhLCr8PkJxLs1Vj9CAsSwisn-k,5141
31
+ ominfra/manage/main.py,sha256=rTY98MrDqGrpwRxo93z5TkfE82Ze3XrL1TqEZ--nkzk,3996
32
+ ominfra/manage/payload.py,sha256=NPuQtMGe1q3kDjG5cgOFmd6M9ITyKRc4IyMRY9PHzRI,818
33
+ ominfra/manage/spawning.py,sha256=EeV4hNcUZOWlM1VuhfGolLHSMWJdz6Dn24OLFtcbisE,2665
32
34
  ominfra/manage/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- ominfra/manage/commands/base.py,sha256=TTrHL213jf-ClVqToiNHuxQey1Yf6052E8u3E9hAf7Q,574
34
- ominfra/manage/commands/subprocess.py,sha256=GpbD-cTorgCRg203lCl81HAh-NBYA6ObKa5ZP2ss9rg,1884
35
+ ominfra/manage/commands/base.py,sha256=nXvqvMYeiuya0hcLnVjQNX3jUdIQN6Brt_UGIQBfNvs,559
36
+ ominfra/manage/commands/subprocess.py,sha256=7d-q8hOVaI5YY-dMU0R1KbCIo3R2mlgBSZ2g8NKEn9A,1941
35
37
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- ominfra/scripts/journald2aws.py,sha256=kCyeyCo53GRoCKAdVAe0aGkgmINxi8IBcUfRXphACek,131618
37
- ominfra/scripts/manage.py,sha256=xgDqnBWLdLogvu2R5e3Pgg3Dc9M2P_6QN1-7MQE5dnY,43817
38
- ominfra/scripts/supervisor.py,sha256=3rhnR5gT3MxqFW9ew5ePYioTDLvEcM2nlYmvbGBCvzI,245884
38
+ ominfra/scripts/journald2aws.py,sha256=wALg2habRTCsXEEACYqzB8qIxmZ0gHSL2NoPfu_iKDk,131771
39
+ ominfra/scripts/manage.py,sha256=nKidMTzCGV0Clfaw3-VlAIpy36LK3afr5gGkPUMaUQo,46670
40
+ ominfra/scripts/supervisor.py,sha256=q8d1LsBIthlv2-cTi1vb9z_2AfZ0f8mE-Xym7TLeiLU,246037
39
41
  ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
40
42
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
41
43
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -77,9 +79,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
77
79
  ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
78
80
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
81
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
80
- ominfra-0.0.0.dev139.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
81
- ominfra-0.0.0.dev139.dist-info/METADATA,sha256=f4A84661QYB6jEMis-4rQWqm5ZajlCBBJ7y367ojJVM,731
82
- ominfra-0.0.0.dev139.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
83
- ominfra-0.0.0.dev139.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
84
- ominfra-0.0.0.dev139.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
85
- ominfra-0.0.0.dev139.dist-info/RECORD,,
82
+ ominfra-0.0.0.dev141.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
83
+ ominfra-0.0.0.dev141.dist-info/METADATA,sha256=CRa6olAYdI24dF7R_HKUHw0aebJQzvHwvACjG82Mff4,731
84
+ ominfra-0.0.0.dev141.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
85
+ ominfra-0.0.0.dev141.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
86
+ ominfra-0.0.0.dev141.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
87
+ ominfra-0.0.0.dev141.dist-info/RECORD,,