ominfra 0.0.0.dev138__tar.gz → 0.0.0.dev140__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. {ominfra-0.0.0.dev138/ominfra.egg-info → ominfra-0.0.0.dev140}/PKG-INFO +3 -3
  2. ominfra-0.0.0.dev138/ominfra/manage/manage.py → ominfra-0.0.0.dev140/ominfra/manage/__init__.py +1 -0
  3. {ominfra-0.0.0.dev138/ominfra/manage/new → ominfra-0.0.0.dev140/ominfra/manage}/commands/base.py +9 -7
  4. {ominfra-0.0.0.dev138/ominfra/manage/new → ominfra-0.0.0.dev140/ominfra/manage}/commands/subprocess.py +20 -15
  5. ominfra-0.0.0.dev140/ominfra/manage/main.py +175 -0
  6. ominfra-0.0.0.dev140/ominfra/manage/payload.py +35 -0
  7. ominfra-0.0.0.dev140/ominfra/manage/spawning.py +100 -0
  8. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/pyremote.py +18 -8
  9. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/scripts/journald2aws.py +7 -0
  10. ominfra-0.0.0.dev138/ominfra/manage/new/_manage.py → ominfra-0.0.0.dev140/ominfra/scripts/manage.py +248 -153
  11. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/scripts/supervisor.py +7 -0
  12. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140/ominfra.egg-info}/PKG-INFO +3 -3
  13. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra.egg-info/SOURCES.txt +7 -34
  14. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra.egg-info/requires.txt +2 -2
  15. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/pyproject.toml +3 -3
  16. ominfra-0.0.0.dev138/ominfra/manage/deploy/_executor.py +0 -1415
  17. ominfra-0.0.0.dev138/ominfra/manage/deploy/configs.py +0 -19
  18. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/base.py +0 -115
  19. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/dirs.py +0 -28
  20. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/nginx.py +0 -47
  21. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/repo.py +0 -17
  22. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/supervisor.py +0 -46
  23. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/systemd.py +0 -88
  24. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/user.py +0 -25
  25. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns/venv.py +0 -22
  26. ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/main.py +0 -119
  27. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/__init__.py +0 -1
  28. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/_main.py +0 -975
  29. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/base.py +0 -178
  30. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/configs.py +0 -38
  31. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/deploy.py +0 -25
  32. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/main.py +0 -18
  33. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/nginx.py +0 -60
  34. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/repo.py +0 -41
  35. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/runtime.py +0 -39
  36. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/site.py +0 -11
  37. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/supervisor.py +0 -64
  38. ominfra-0.0.0.dev138/ominfra/manage/deploy/poly/venv.py +0 -52
  39. ominfra-0.0.0.dev138/ominfra/manage/deploy/remote.py +0 -91
  40. ominfra-0.0.0.dev138/ominfra/manage/new/__init__.py +0 -1
  41. ominfra-0.0.0.dev138/ominfra/manage/new/main.py +0 -234
  42. ominfra-0.0.0.dev138/ominfra/supervisor/__init__.py +0 -1
  43. ominfra-0.0.0.dev138/ominfra/supervisor/utils/__init__.py +0 -0
  44. ominfra-0.0.0.dev138/ominfra/tailscale/__init__.py +0 -0
  45. ominfra-0.0.0.dev138/ominfra/tools/__init__.py +0 -0
  46. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/LICENSE +0 -0
  47. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/MANIFEST.in +0 -0
  48. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/README.rst +0 -0
  49. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/.manifests.json +0 -0
  50. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/__about__.py +0 -0
  51. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/__init__.py +0 -0
  52. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/__init__.py +0 -0
  53. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/__init__.py +0 -0
  54. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/__main__.py +0 -0
  55. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/auth.py +0 -0
  56. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/cli.py +0 -0
  57. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/dataclasses.py +0 -0
  58. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  59. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  60. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  61. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
  62. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/journald2aws/main.py +0 -0
  63. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  64. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/logs.py +0 -0
  65. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/aws/metadata.py +0 -0
  66. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/gcp/__init__.py +0 -0
  67. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/clouds/gcp/auth.py +0 -0
  68. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/cmds.py +0 -0
  69. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/configs.py +0 -0
  70. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/journald/__init__.py +0 -0
  71. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/journald/fields.py +0 -0
  72. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/journald/genmessages.py +0 -0
  73. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/journald/messages.py +0 -0
  74. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/journald/tailer.py +0 -0
  75. {ominfra-0.0.0.dev138/ominfra/manage → ominfra-0.0.0.dev140/ominfra/manage/commands}/__init__.py +0 -0
  76. {ominfra-0.0.0.dev138/ominfra/manage/deploy → ominfra-0.0.0.dev140/ominfra/scripts}/__init__.py +0 -0
  77. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/ssh.py +0 -0
  78. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/LICENSE.txt +0 -0
  79. {ominfra-0.0.0.dev138/ominfra/manage/deploy/executor → ominfra-0.0.0.dev140/ominfra/supervisor}/__init__.py +0 -0
  80. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/__main__.py +0 -0
  81. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/configs.py +0 -0
  82. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/dispatchers.py +0 -0
  83. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/dispatchersimpl.py +0 -0
  84. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/events.py +0 -0
  85. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/exceptions.py +0 -0
  86. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/groups.py +0 -0
  87. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/groupsimpl.py +0 -0
  88. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/http.py +0 -0
  89. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/inject.py +0 -0
  90. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/io.py +0 -0
  91. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/main.py +0 -0
  92. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/pipes.py +0 -0
  93. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/privileges.py +0 -0
  94. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/process.py +0 -0
  95. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/processimpl.py +0 -0
  96. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/setup.py +0 -0
  97. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/setupimpl.py +0 -0
  98. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/signals.py +0 -0
  99. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/spawning.py +0 -0
  100. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/spawningimpl.py +0 -0
  101. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/states.py +0 -0
  102. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/supervisor.py +0 -0
  103. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/types.py +0 -0
  104. {ominfra-0.0.0.dev138/ominfra/manage/deploy/executor/concerns → ominfra-0.0.0.dev140/ominfra/supervisor/utils}/__init__.py +0 -0
  105. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/collections.py +0 -0
  106. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/diag.py +0 -0
  107. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/fds.py +0 -0
  108. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/fs.py +0 -0
  109. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/os.py +0 -0
  110. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/ostypes.py +0 -0
  111. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/signals.py +0 -0
  112. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/strings.py +0 -0
  113. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/supervisor/utils/users.py +0 -0
  114. {ominfra-0.0.0.dev138/ominfra/manage/new/commands → ominfra-0.0.0.dev140/ominfra/tailscale}/__init__.py +0 -0
  115. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/tailscale/api.py +0 -0
  116. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/tailscale/cli.py +0 -0
  117. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/threadworkers.py +0 -0
  118. {ominfra-0.0.0.dev138/ominfra/scripts → ominfra-0.0.0.dev140/ominfra/tools}/__init__.py +0 -0
  119. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra/tools/listresources.py +0 -0
  120. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra.egg-info/dependency_links.txt +0 -0
  121. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra.egg-info/entry_points.txt +0 -0
  122. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/ominfra.egg-info/top_level.txt +0 -0
  123. {ominfra-0.0.0.dev138 → ominfra-0.0.0.dev140}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev138
3
+ Version: 0.0.0.dev140
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.dev138
16
- Requires-Dist: omlish==0.0.0.dev138
15
+ Requires-Dist: omdev==0.0.0.dev140
16
+ Requires-Dist: omlish==0.0.0.dev140
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
- 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
 
@@ -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 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