ominfra 0.0.0.dev139__py3-none-any.whl → 0.0.0.dev141__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.
@@ -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,,