ominfra 0.0.0.dev139__tar.gz → 0.0.0.dev141__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {ominfra-0.0.0.dev139/ominfra.egg-info → ominfra-0.0.0.dev141}/PKG-INFO +3 -3
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/manage/commands/base.py +9 -7
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/manage/commands/subprocess.py +20 -15
- ominfra-0.0.0.dev141/ominfra/manage/main.py +175 -0
- ominfra-0.0.0.dev141/ominfra/manage/payload.py +35 -0
- ominfra-0.0.0.dev141/ominfra/manage/spawning.py +100 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/pyremote.py +18 -8
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/scripts/journald2aws.py +7 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/scripts/manage.py +236 -141
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/scripts/supervisor.py +7 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141/ominfra.egg-info}/PKG-INFO +3 -3
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra.egg-info/SOURCES.txt +2 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra.egg-info/requires.txt +2 -2
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/pyproject.toml +3 -3
- ominfra-0.0.0.dev139/ominfra/manage/main.py +0 -234
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/LICENSE +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/MANIFEST.in +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/README.rst +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/.manifests.json +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/__about__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/__main__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/auth.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/cli.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/dataclasses.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/journald2aws/main.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/logs.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/aws/metadata.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/gcp/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/clouds/gcp/auth.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/cmds.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/configs.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/journald/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/journald/fields.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/journald/genmessages.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/journald/messages.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/journald/tailer.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/manage/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/manage/commands/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/scripts/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/ssh.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/LICENSE.txt +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/__main__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/configs.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/dispatchers.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/dispatchersimpl.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/events.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/exceptions.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/groups.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/groupsimpl.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/http.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/inject.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/io.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/main.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/pipes.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/privileges.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/process.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/processimpl.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/setup.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/setupimpl.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/signals.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/spawning.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/spawningimpl.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/states.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/supervisor.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/types.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/collections.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/diag.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/fds.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/fs.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/os.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/ostypes.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/signals.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/strings.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/supervisor/utils/users.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/tailscale/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/tailscale/api.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/tailscale/cli.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/threadworkers.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/tools/__init__.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra/tools/listresources.py +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra.egg-info/dependency_links.txt +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra.egg-info/entry_points.txt +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/ominfra.egg-info/top_level.txt +0 -0
- {ominfra-0.0.0.dev139 → ominfra-0.0.0.dev141}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
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.
|
16
|
-
Requires-Dist: omlish==0.0.0.
|
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"
|
@@ -4,22 +4,24 @@ import dataclasses as dc
|
|
4
4
|
import typing as ta
|
5
5
|
|
6
6
|
|
7
|
-
|
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
|
-
|
15
|
-
|
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
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
args: ta.Sequence[str]
|
17
|
+
@dc.dataclass(frozen=True)
|
18
|
+
class SubprocessCommand(Command['SubprocessCommand.Output']):
|
19
|
+
args: ta.Sequence[str]
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
shell: bool = False
|
22
|
+
cwd: ta.Optional[str] = None
|
23
|
+
env: ta.Optional[ta.Mapping[str, str]] = None
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
capture_stdout: bool = False
|
26
|
+
capture_stderr: bool = False
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
input: ta.Optional[bytes] = None
|
29
|
+
timeout: ta.Optional[float] = None
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
|
@@ -0,0 +1,175 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# @omlish-amalg ../scripts/manage.py
|
3
|
+
# ruff: noqa: UP006 UP007
|
4
|
+
"""
|
5
|
+
manage.py -s 'docker run -i python:3.12'
|
6
|
+
manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
|
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
|
+
##
|
87
|
+
|
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)
|
103
|
+
|
104
|
+
|
105
|
+
##
|
106
|
+
|
107
|
+
|
108
|
+
def _main() -> None:
|
109
|
+
import argparse
|
110
|
+
|
111
|
+
parser = argparse.ArgumentParser()
|
112
|
+
|
113
|
+
parser.add_argument('-s', '--shell')
|
114
|
+
parser.add_argument('-q', '--shell-quote', action='store_true')
|
115
|
+
parser.add_argument('--python', default='python3')
|
116
|
+
parser.add_argument('--debug', action='store_true')
|
117
|
+
parser.add_argument('--_payload-file')
|
118
|
+
|
119
|
+
args = parser.parse_args()
|
120
|
+
|
121
|
+
#
|
122
|
+
|
123
|
+
payload_src = get_payload_src(file=args._payload_file) # noqa
|
124
|
+
|
125
|
+
#
|
126
|
+
|
127
|
+
remote_src = '\n\n'.join([
|
128
|
+
'__name__ = "__remote__"',
|
129
|
+
payload_src,
|
130
|
+
'_remote_main()',
|
131
|
+
])
|
132
|
+
|
133
|
+
#
|
134
|
+
|
135
|
+
bs_src = pyremote_build_bootstrap_cmd(__package__ or 'manage')
|
136
|
+
|
137
|
+
#
|
138
|
+
|
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)
|
172
|
+
|
173
|
+
|
174
|
+
if __name__ == '__main__':
|
175
|
+
_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)
|
@@ -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
|
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
|
274
|
-
if
|
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
|
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.
|
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(
|
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
|
|