ominfra 0.0.0.dev137__tar.gz → 0.0.0.dev138__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. {ominfra-0.0.0.dev137/ominfra.egg-info → ominfra-0.0.0.dev138}/PKG-INFO +3 -3
  2. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/new/_manage.py +254 -157
  3. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/new/main.py +63 -13
  4. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/pyremote.py +196 -145
  5. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/scripts/supervisor.py +32 -31
  6. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/processimpl.py +32 -31
  7. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138/ominfra.egg-info}/PKG-INFO +3 -3
  8. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra.egg-info/requires.txt +2 -2
  9. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/pyproject.toml +3 -3
  10. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/LICENSE +0 -0
  11. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/MANIFEST.in +0 -0
  12. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/README.rst +0 -0
  13. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/.manifests.json +0 -0
  14. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/__about__.py +0 -0
  15. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/__init__.py +0 -0
  16. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/__init__.py +0 -0
  17. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/__init__.py +0 -0
  18. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/__main__.py +0 -0
  19. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/auth.py +0 -0
  20. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/cli.py +0 -0
  21. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/dataclasses.py +0 -0
  22. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  23. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  24. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  25. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
  26. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/journald2aws/main.py +0 -0
  27. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  28. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/logs.py +0 -0
  29. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/aws/metadata.py +0 -0
  30. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/gcp/__init__.py +0 -0
  31. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/clouds/gcp/auth.py +0 -0
  32. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/cmds.py +0 -0
  33. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/configs.py +0 -0
  34. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/journald/__init__.py +0 -0
  35. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/journald/fields.py +0 -0
  36. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/journald/genmessages.py +0 -0
  37. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/journald/messages.py +0 -0
  38. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/journald/tailer.py +0 -0
  39. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/__init__.py +0 -0
  40. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/__init__.py +0 -0
  41. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/_executor.py +0 -0
  42. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/configs.py +0 -0
  43. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/__init__.py +0 -0
  44. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/base.py +0 -0
  45. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/__init__.py +0 -0
  46. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/dirs.py +0 -0
  47. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/nginx.py +0 -0
  48. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/repo.py +0 -0
  49. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/supervisor.py +0 -0
  50. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/systemd.py +0 -0
  51. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/user.py +0 -0
  52. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/concerns/venv.py +0 -0
  53. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/executor/main.py +0 -0
  54. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/__init__.py +0 -0
  55. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/_main.py +0 -0
  56. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/base.py +0 -0
  57. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/configs.py +0 -0
  58. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/deploy.py +0 -0
  59. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/main.py +0 -0
  60. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/nginx.py +0 -0
  61. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/repo.py +0 -0
  62. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/runtime.py +0 -0
  63. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/site.py +0 -0
  64. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/supervisor.py +0 -0
  65. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/poly/venv.py +0 -0
  66. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/deploy/remote.py +0 -0
  67. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/manage.py +0 -0
  68. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/new/__init__.py +0 -0
  69. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/new/commands/__init__.py +0 -0
  70. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/new/commands/base.py +0 -0
  71. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/manage/new/commands/subprocess.py +0 -0
  72. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/scripts/__init__.py +0 -0
  73. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/scripts/journald2aws.py +0 -0
  74. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/ssh.py +0 -0
  75. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/LICENSE.txt +0 -0
  76. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/__init__.py +0 -0
  77. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/__main__.py +0 -0
  78. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/configs.py +0 -0
  79. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/dispatchers.py +0 -0
  80. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/dispatchersimpl.py +0 -0
  81. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/events.py +0 -0
  82. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/exceptions.py +0 -0
  83. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/groups.py +0 -0
  84. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/groupsimpl.py +0 -0
  85. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/http.py +0 -0
  86. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/inject.py +0 -0
  87. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/io.py +0 -0
  88. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/main.py +0 -0
  89. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/pipes.py +0 -0
  90. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/privileges.py +0 -0
  91. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/process.py +0 -0
  92. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/setup.py +0 -0
  93. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/setupimpl.py +0 -0
  94. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/signals.py +0 -0
  95. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/spawning.py +0 -0
  96. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/spawningimpl.py +0 -0
  97. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/states.py +0 -0
  98. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/supervisor.py +0 -0
  99. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/types.py +0 -0
  100. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/__init__.py +0 -0
  101. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/collections.py +0 -0
  102. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/diag.py +0 -0
  103. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/fds.py +0 -0
  104. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/fs.py +0 -0
  105. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/os.py +0 -0
  106. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/ostypes.py +0 -0
  107. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/signals.py +0 -0
  108. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/strings.py +0 -0
  109. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/supervisor/utils/users.py +0 -0
  110. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/tailscale/__init__.py +0 -0
  111. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/tailscale/api.py +0 -0
  112. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/tailscale/cli.py +0 -0
  113. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/threadworkers.py +0 -0
  114. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/tools/__init__.py +0 -0
  115. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra/tools/listresources.py +0 -0
  116. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra.egg-info/SOURCES.txt +0 -0
  117. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra.egg-info/dependency_links.txt +0 -0
  118. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra.egg-info/entry_points.txt +0 -0
  119. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/ominfra.egg-info/top_level.txt +0 -0
  120. {ominfra-0.0.0.dev137 → ominfra-0.0.0.dev138}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev137
3
+ Version: 0.0.0.dev138
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.dev137
16
- Requires-Dist: omlish==0.0.0.dev137
15
+ Requires-Dist: omdev==0.0.0.dev138
16
+ Requires-Dist: omlish==0.0.0.dev138
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -4,6 +4,10 @@
4
4
  # @omlish-script
5
5
  # @omlish-amalg-output main.py
6
6
  # ruff: noqa: N802 UP006 UP007 UP036
7
+ """
8
+ manage.py -s 'docker run -i python:3.12'
9
+ manage.py -qs 'ssh -i foo/bar foo@bar.baz' --python=python3.8
10
+ """
7
11
  import abc
8
12
  import base64
9
13
  import collections.abc
@@ -25,7 +29,6 @@ import site
25
29
  import struct
26
30
  import subprocess
27
31
  import sys
28
- import textwrap
29
32
  import threading
30
33
  import time
31
34
  import types
@@ -87,11 +90,120 @@ Basically this: https://mitogen.networkgenomics.com/howitworks.html
87
90
  ##
88
91
 
89
92
 
93
+ @dc.dataclass(frozen=True)
94
+ class PyremoteBootstrapOptions:
95
+ debug: bool = False
96
+
97
+
98
+ ##
99
+
100
+
101
+ @dc.dataclass(frozen=True)
102
+ class PyremoteEnvInfo:
103
+ sys_base_prefix: str
104
+ sys_byteorder: str
105
+ sys_defaultencoding: str
106
+ sys_exec_prefix: str
107
+ sys_executable: str
108
+ sys_implementation_name: str
109
+ sys_path: ta.List[str]
110
+ sys_platform: str
111
+ sys_prefix: str
112
+ sys_version: str
113
+ sys_version_info: ta.List[ta.Union[int, str]]
114
+
115
+ platform_architecture: ta.List[str]
116
+ platform_machine: str
117
+ platform_platform: str
118
+ platform_processor: str
119
+ platform_system: str
120
+ platform_release: str
121
+ platform_version: str
122
+
123
+ site_userbase: str
124
+
125
+ os_cwd: str
126
+ os_gid: int
127
+ os_loadavg: ta.List[float]
128
+ os_login: ta.Optional[str]
129
+ os_pgrp: int
130
+ os_pid: int
131
+ os_ppid: int
132
+ os_uid: int
133
+
134
+ pw_name: str
135
+ pw_uid: int
136
+ pw_gid: int
137
+ pw_gecos: str
138
+ pw_dir: str
139
+ pw_shell: str
140
+
141
+ env_path: ta.Optional[str]
142
+
143
+
144
+ def _get_pyremote_env_info() -> PyremoteEnvInfo:
145
+ os_uid = os.getuid()
146
+
147
+ pw = pwd.getpwuid(os_uid)
148
+
149
+ os_login: ta.Optional[str]
150
+ try:
151
+ os_login = os.getlogin()
152
+ except OSError:
153
+ os_login = None
154
+
155
+ return PyremoteEnvInfo(
156
+ sys_base_prefix=sys.base_prefix,
157
+ sys_byteorder=sys.byteorder,
158
+ sys_defaultencoding=sys.getdefaultencoding(),
159
+ sys_exec_prefix=sys.exec_prefix,
160
+ sys_executable=sys.executable,
161
+ sys_implementation_name=sys.implementation.name,
162
+ sys_path=sys.path,
163
+ sys_platform=sys.platform,
164
+ sys_prefix=sys.prefix,
165
+ sys_version=sys.version,
166
+ sys_version_info=list(sys.version_info),
167
+
168
+ platform_architecture=list(platform.architecture()),
169
+ platform_machine=platform.machine(),
170
+ platform_platform=platform.platform(),
171
+ platform_processor=platform.processor(),
172
+ platform_system=platform.system(),
173
+ platform_release=platform.release(),
174
+ platform_version=platform.version(),
175
+
176
+ site_userbase=site.getuserbase(),
177
+
178
+ os_cwd=os.getcwd(),
179
+ os_gid=os.getgid(),
180
+ os_loadavg=list(os.getloadavg()),
181
+ os_login=os_login,
182
+ os_pgrp=os.getpgrp(),
183
+ os_pid=os.getpid(),
184
+ os_ppid=os.getppid(),
185
+ os_uid=os_uid,
186
+
187
+ pw_name=pw.pw_name,
188
+ pw_uid=pw.pw_uid,
189
+ pw_gid=pw.pw_gid,
190
+ pw_gecos=pw.pw_gecos,
191
+ pw_dir=pw.pw_dir,
192
+ pw_shell=pw.pw_shell,
193
+
194
+ env_path=os.environ.get('PATH'),
195
+ )
196
+
197
+
198
+ ##
199
+
200
+
90
201
  _PYREMOTE_BOOTSTRAP_INPUT_FD = 100
91
202
  _PYREMOTE_BOOTSTRAP_SRC_FD = 101
92
203
 
93
204
  _PYREMOTE_BOOTSTRAP_CHILD_PID_VAR = '_OPYR_CHILD_PID'
94
205
  _PYREMOTE_BOOTSTRAP_ARGV0_VAR = '_OPYR_ARGV0'
206
+ _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR = '_OPYR_OPTIONS_JSON'
95
207
 
96
208
  _PYREMOTE_BOOTSTRAP_ACK0 = b'OPYR000\n'
97
209
  _PYREMOTE_BOOTSTRAP_ACK1 = b'OPYR001\n'
@@ -153,7 +265,9 @@ def _pyremote_bootstrap_main(context_name: str) -> None:
153
265
 
154
266
  # Read main src from stdin
155
267
  main_z_len = struct.unpack('<I', os.read(0, 4))[0]
156
- main_src = zlib.decompress(os.fdopen(0, 'rb').read(main_z_len))
268
+ if len(main_z := os.fdopen(0, 'rb').read(main_z_len)) != main_z_len:
269
+ raise EOFError
270
+ main_src = zlib.decompress(main_z)
157
271
 
158
272
  # Write both copies of main src. Must write to w0 (parent stdin) before w1 (copy pipe) as pipe will likely fill
159
273
  # and block and need to be drained by pyremote_bootstrap_finalize running in parent.
@@ -176,6 +290,8 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
176
290
  if any(c in context_name for c in '\'"'):
177
291
  raise NameError(context_name)
178
292
 
293
+ import inspect
294
+ import textwrap
179
295
  bs_src = textwrap.dedent(inspect.getsource(_pyremote_bootstrap_main))
180
296
 
181
297
  for gl in [
@@ -202,9 +318,6 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
202
318
  bs_z = zlib.compress(bs_src.encode('utf-8'))
203
319
  bs_z64 = base64.encodebytes(bs_z).replace(b'\n', b'')
204
320
 
205
- def dq_repr(o: ta.Any) -> str:
206
- return '"' + repr(o)[1:-1] + '"'
207
-
208
321
  stmts = [
209
322
  f'import {", ".join(_PYREMOTE_BOOTSTRAP_IMPORTS)}',
210
323
  f'exec(zlib.decompress(base64.decodebytes({bs_z64!r})))',
@@ -219,99 +332,83 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
219
332
 
220
333
 
221
334
  @dc.dataclass(frozen=True)
222
- class PyremoteEnvInfo:
223
- sys_base_prefix: str
224
- sys_byteorder: str
225
- sys_defaultencoding: str
226
- sys_exec_prefix: str
227
- sys_executable: str
228
- sys_implementation_name: str
229
- sys_path: ta.List[str]
230
- sys_platform: str
231
- sys_prefix: str
232
- sys_version: str
233
- sys_version_info: ta.List[ta.Union[int, str]]
234
-
235
- platform_architecture: ta.List[str]
236
- platform_machine: str
237
- platform_platform: str
238
- platform_processor: str
239
- platform_system: str
240
- platform_release: str
241
- platform_version: str
242
-
243
- site_userbase: str
244
-
245
- os_cwd: str
246
- os_gid: int
247
- os_loadavg: ta.List[float]
248
- os_login: ta.Optional[str]
249
- os_pgrp: int
250
- os_pid: int
251
- os_ppid: int
252
- os_uid: int
253
-
254
- pw_name: str
255
- pw_uid: int
256
- pw_gid: int
257
- pw_gecos: str
258
- pw_dir: str
259
- pw_shell: str
260
-
261
- env_path: ta.Optional[str]
335
+ class PyremotePayloadRuntime:
336
+ input: ta.BinaryIO
337
+ output: ta.BinaryIO
338
+ main_src: str
339
+ options: PyremoteBootstrapOptions
340
+ env_info: PyremoteEnvInfo
262
341
 
263
342
 
264
- def _get_pyremote_env_info() -> PyremoteEnvInfo:
265
- os_uid = os.getuid()
343
+ 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:
346
+ # Read second copy of main src
347
+ r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
348
+ main_src = r1.read().decode('utf-8')
349
+ r1.close()
350
+
351
+ # Reap boostrap child. Must be done after reading second copy of source because source may be too big to fit in
352
+ # a pipe at once.
353
+ os.waitpid(int(os.environ.pop(_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR)), 0)
354
+
355
+ # Read options
356
+ options_json_len = struct.unpack('<I', os.read(_PYREMOTE_BOOTSTRAP_INPUT_FD, 4))[0]
357
+ if len(options_json := os.read(_PYREMOTE_BOOTSTRAP_INPUT_FD, options_json_len)) != options_json_len:
358
+ raise EOFError
359
+ options = PyremoteBootstrapOptions(**json.loads(options_json.decode('utf-8')))
360
+
361
+ # If debugging, re-exec as file
362
+ if options.debug:
363
+ # Write temp source file
364
+ import tempfile
365
+ tfd, tfn = tempfile.mkstemp('-pyremote.py')
366
+ os.write(tfd, main_src.encode('utf-8'))
367
+ os.close(tfd)
368
+
369
+ # Set json options var
370
+ os.environ[_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR] = options_json.decode('utf-8')
371
+
372
+ # Re-exec temp file
373
+ os.execl(os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR], sys.orig_argv[0], tfn)
266
374
 
267
- pw = pwd.getpwuid(os_uid)
375
+ else:
376
+ # Load options json var
377
+ options_json_str = os.environ.pop(_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR)
378
+ options = PyremoteBootstrapOptions(**json.loads(options_json_str))
268
379
 
269
- os_login: ta.Optional[str]
270
- try:
271
- os_login = os.getlogin()
272
- except OSError:
273
- os_login = None
380
+ # Read temp source file
381
+ with open(sys.orig_argv[1]) as sf:
382
+ main_src = sf.read()
274
383
 
275
- return PyremoteEnvInfo(
276
- sys_base_prefix=sys.base_prefix,
277
- sys_byteorder=sys.byteorder,
278
- sys_defaultencoding=sys.getdefaultencoding(),
279
- sys_exec_prefix=sys.exec_prefix,
280
- sys_executable=sys.executable,
281
- sys_implementation_name=sys.implementation.name,
282
- sys_path=sys.path,
283
- sys_platform=sys.platform,
284
- sys_prefix=sys.prefix,
285
- sys_version=sys.version,
286
- sys_version_info=list(sys.version_info),
384
+ # Restore original argv0
385
+ sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
287
386
 
288
- platform_architecture=list(platform.architecture()),
289
- platform_machine=platform.machine(),
290
- platform_platform=platform.platform(),
291
- platform_processor=platform.processor(),
292
- platform_system=platform.system(),
293
- platform_release=platform.release(),
294
- platform_version=platform.version(),
387
+ # Write third ack
388
+ os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
295
389
 
296
- site_userbase=site.getuserbase(),
390
+ # Write env info
391
+ env_info = _get_pyremote_env_info()
392
+ env_info_json = json.dumps(dc.asdict(env_info), indent=None, separators=(',', ':')) # noqa
393
+ os.write(1, struct.pack('<I', len(env_info_json)))
394
+ os.write(1, env_info_json.encode('utf-8'))
297
395
 
298
- os_cwd=os.getcwd(),
299
- os_gid=os.getgid(),
300
- os_loadavg=list(os.getloadavg()),
301
- os_login=os_login,
302
- os_pgrp=os.getpgrp(),
303
- os_pid=os.getpid(),
304
- os_ppid=os.getppid(),
305
- os_uid=os_uid,
396
+ # Setup IO
397
+ input = os.fdopen(_PYREMOTE_BOOTSTRAP_INPUT_FD, 'rb', 0) # noqa
398
+ output = os.fdopen(os.dup(1), 'wb', 0) # noqa
399
+ os.dup2(nfd := os.open('/dev/null', os.O_WRONLY), 1)
400
+ os.close(nfd)
306
401
 
307
- pw_name=pw.pw_name,
308
- pw_uid=pw.pw_uid,
309
- pw_gid=pw.pw_gid,
310
- pw_gecos=pw.pw_gecos,
311
- pw_dir=pw.pw_dir,
312
- pw_shell=pw.pw_shell,
402
+ # Write fourth ack
403
+ output.write(_PYREMOTE_BOOTSTRAP_ACK3)
313
404
 
314
- env_path=os.environ.get('PATH'),
405
+ # Return
406
+ return PyremotePayloadRuntime(
407
+ input=input,
408
+ output=output,
409
+ main_src=main_src,
410
+ options=options,
411
+ env_info=env_info,
315
412
  )
316
413
 
317
414
 
@@ -319,12 +416,15 @@ def _get_pyremote_env_info() -> PyremoteEnvInfo:
319
416
 
320
417
 
321
418
  class PyremoteBootstrapDriver:
322
- def __init__(self, main_src: str) -> None:
419
+ def __init__(self, main_src: str, options: PyremoteBootstrapOptions = PyremoteBootstrapOptions()) -> None:
323
420
  super().__init__()
324
421
 
325
422
  self._main_src = main_src
326
423
  self._main_z = zlib.compress(main_src.encode('utf-8'))
327
424
 
425
+ self._options = options
426
+ self._options_json = json.dumps(dc.asdict(options), indent=None, separators=(',', ':')).encode('utf-8') # noqa
427
+
328
428
  #
329
429
 
330
430
  @dc.dataclass(frozen=True)
@@ -344,7 +444,7 @@ class PyremoteBootstrapDriver:
344
444
  env_info: PyremoteEnvInfo
345
445
 
346
446
  def gen(self) -> ta.Generator[ta.Union[Read, Write], ta.Optional[bytes], Result]:
347
- # Read first ack
447
+ # Read first ack (after fork)
348
448
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK0)
349
449
 
350
450
  # Read pid
@@ -355,8 +455,14 @@ class PyremoteBootstrapDriver:
355
455
  yield from self._write(struct.pack('<I', len(self._main_z)))
356
456
  yield from self._write(self._main_z)
357
457
 
358
- # Read second and third ack
458
+ # Read second ack (after writing src copies)
359
459
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK1)
460
+
461
+ # Write options
462
+ yield from self._write(struct.pack('<I', len(self._options_json)))
463
+ yield from self._write(self._options_json)
464
+
465
+ # Read third ack (after reaping child process)
360
466
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK2)
361
467
 
362
468
  # Read env info
@@ -366,7 +472,7 @@ class PyremoteBootstrapDriver:
366
472
  env_info_json = d.decode('utf-8')
367
473
  env_info = PyremoteEnvInfo(**json.loads(env_info_json))
368
474
 
369
- # Read fourth ack
475
+ # Read fourth ack (after finalization completed)
370
476
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK3)
371
477
 
372
478
  # Return
@@ -409,7 +515,8 @@ class PyremoteBootstrapDriver:
409
515
  return e.value
410
516
 
411
517
  if isinstance(go, self.Read):
412
- gi = stdout.read(go.sz)
518
+ if len(gi := stdout.read(go.sz)) != go.sz:
519
+ raise EOFError
413
520
  elif isinstance(go, self.Write):
414
521
  gi = None
415
522
  stdin.write(go.d)
@@ -418,57 +525,6 @@ class PyremoteBootstrapDriver:
418
525
  raise TypeError(go)
419
526
 
420
527
 
421
- ##
422
-
423
-
424
- @dc.dataclass(frozen=True)
425
- class PyremotePayloadRuntime:
426
- input: ta.BinaryIO
427
- output: ta.BinaryIO
428
- main_src: str
429
- env_info: PyremoteEnvInfo
430
-
431
-
432
- def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
433
- # Restore original argv0
434
- sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
435
-
436
- # Read second copy of main src
437
- r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
438
- main_src = r1.read().decode('utf-8')
439
- r1.close()
440
-
441
- # Reap boostrap child. Must be done after reading second copy of source because source may be too big to fit in a
442
- # pipe at once.
443
- os.waitpid(int(os.environ.pop(_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR)), 0)
444
-
445
- # Write third ack
446
- os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
447
-
448
- # Write env info
449
- env_info = _get_pyremote_env_info()
450
- env_info_json = json.dumps(dc.asdict(env_info), indent=None, separators=(',', ':')) # noqa
451
- os.write(1, struct.pack('<I', len(env_info_json)))
452
- os.write(1, env_info_json.encode('utf-8'))
453
-
454
- # Setup IO
455
- input = os.fdopen(_PYREMOTE_BOOTSTRAP_INPUT_FD, 'rb', 0) # noqa
456
- output = os.fdopen(os.dup(1), 'wb', 0) # noqa
457
- os.dup2(nfd := os.open('/dev/null', os.O_WRONLY), 1)
458
- os.close(nfd)
459
-
460
- # Write fourth ack
461
- output.write(_PYREMOTE_BOOTSTRAP_ACK3)
462
-
463
- # Return
464
- return PyremotePayloadRuntime(
465
- input=input,
466
- output=output,
467
- main_src=main_src,
468
- env_info=env_info,
469
- )
470
-
471
-
472
528
  ########################################
473
529
  # ../../../../omlish/lite/cached.py
474
530
 
@@ -1509,8 +1565,41 @@ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Ou
1509
1565
  ##
1510
1566
 
1511
1567
 
1512
- def _send_obj(f: ta.IO, o: ta.Any) -> None:
1513
- j = json_dumps_compact(marshal_obj(o))
1568
+ _COMMAND_TYPES = {
1569
+ 'subprocess': SubprocessCommand,
1570
+ }
1571
+
1572
+
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),
1592
+ )
1593
+ for k, cty in _COMMAND_TYPES.items()
1594
+ ]),
1595
+ )
1596
+
1597
+
1598
+ ##
1599
+
1600
+
1601
+ def _send_obj(f: ta.IO, o: ta.Any, ty: ta.Any = None) -> None:
1602
+ j = json_dumps_compact(marshal_obj(o, ty))
1514
1603
  d = j.encode('utf-8')
1515
1604
 
1516
1605
  f.write(struct.pack('<I', len(d)))
@@ -1518,17 +1607,17 @@ def _send_obj(f: ta.IO, o: ta.Any) -> None:
1518
1607
  f.flush()
1519
1608
 
1520
1609
 
1521
- def _recv_obj(f: ta.IO, ty: type) -> ta.Any:
1610
+ def _recv_obj(f: ta.IO, ty: ta.Any) -> ta.Any:
1522
1611
  d = f.read(4)
1523
1612
  if not d:
1524
1613
  return None
1525
1614
  if len(d) != 4:
1526
- raise Exception
1615
+ raise EOFError
1527
1616
 
1528
1617
  sz = struct.unpack('<I', d)[0]
1529
1618
  d = f.read(sz)
1530
- if not d:
1531
- raise Exception
1619
+ if len(d) != sz:
1620
+ raise EOFError
1532
1621
 
1533
1622
  j = json.loads(d.decode('utf-8'))
1534
1623
  return unmarshal_obj(j, ty)
@@ -1541,13 +1630,16 @@ def _remote_main() -> None:
1541
1630
  rt = pyremote_bootstrap_finalize() # noqa
1542
1631
 
1543
1632
  while True:
1544
- i = _recv_obj(rt.input, SubprocessCommand.Input)
1633
+ i = _recv_obj(rt.input, Command.Input)
1545
1634
  if i is None:
1546
1635
  break
1547
1636
 
1548
- o = SubprocessCommand()._execute(i) # noqa
1637
+ if isinstance(i, SubprocessCommand.Input):
1638
+ o = SubprocessCommand()._execute(i) # noqa
1639
+ else:
1640
+ raise TypeError(i)
1549
1641
 
1550
- _send_obj(rt.output, o)
1642
+ _send_obj(rt.output, o, Command.Output)
1551
1643
 
1552
1644
 
1553
1645
  ##
@@ -1634,8 +1726,13 @@ def _main() -> None:
1634
1726
  stdin = check_not_none(proc.stdin)
1635
1727
  stdout = check_not_none(proc.stdout)
1636
1728
 
1637
- res = PyremoteBootstrapDriver(remote_src).run(stdin, stdout)
1638
- print(res)
1729
+ res = PyremoteBootstrapDriver( # noqa
1730
+ remote_src,
1731
+ PyremoteBootstrapOptions(
1732
+ # debug=True,
1733
+ ),
1734
+ ).run(stdin, stdout)
1735
+ # print(res)
1639
1736
 
1640
1737
  #
1641
1738
 
@@ -1650,9 +1747,9 @@ def _main() -> None:
1650
1747
  capture_stdout=True,
1651
1748
  ),
1652
1749
  ]:
1653
- _send_obj(stdin, ci)
1750
+ _send_obj(stdin, ci, Command.Input)
1654
1751
 
1655
- o = _recv_obj(stdout, SubprocessCommand.Output)
1752
+ o = _recv_obj(stdout, Command.Output)
1656
1753
 
1657
1754
  print(o)
1658
1755