ominfra 0.0.0.dev136__py3-none-any.whl → 0.0.0.dev138__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.
@@ -1,32 +1,74 @@
1
1
  #!/usr/bin/env python3
2
2
  # @omlish-amalg ./_manage.py
3
3
  # ruff: noqa: UP006 UP007
4
+ """
5
+ manage.py -s 'docker run -i python:3.12'
6
+ manage.py -qs 'ssh -i foo/bar foo@bar.baz' --python=python3.8
7
+ """
4
8
  import inspect
5
9
  import json
6
- import os
7
10
  import shlex
8
11
  import struct
9
12
  import subprocess
10
13
  import sys
11
14
  import typing as ta
12
15
 
16
+ from omlish.lite.cached import cached_nullary
13
17
  from omlish.lite.check import check_not_none
14
18
  from omlish.lite.json import json_dumps_compact
19
+ from omlish.lite.marshal import PolymorphicObjMarshaler
20
+ from omlish.lite.marshal import get_obj_marshaler
15
21
  from omlish.lite.marshal import marshal_obj
22
+ from omlish.lite.marshal import register_opj_marshaler
16
23
  from omlish.lite.marshal import unmarshal_obj
17
24
  from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
18
25
 
19
26
  from ...pyremote import PyremoteBootstrapDriver
27
+ from ...pyremote import PyremoteBootstrapOptions
20
28
  from ...pyremote import pyremote_bootstrap_finalize
21
29
  from ...pyremote import pyremote_build_bootstrap_cmd
30
+ from .commands.base import Command
22
31
  from .commands.subprocess import SubprocessCommand
23
32
 
24
33
 
25
34
  ##
26
35
 
27
36
 
28
- def _send_obj(f: ta.IO, o: ta.Any) -> None:
29
- j = json_dumps_compact(marshal_obj(o))
37
+ _COMMAND_TYPES = {
38
+ 'subprocess': SubprocessCommand,
39
+ }
40
+
41
+
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),
61
+ )
62
+ for k, cty in _COMMAND_TYPES.items()
63
+ ]),
64
+ )
65
+
66
+
67
+ ##
68
+
69
+
70
+ def _send_obj(f: ta.IO, o: ta.Any, ty: ta.Any = None) -> None:
71
+ j = json_dumps_compact(marshal_obj(o, ty))
30
72
  d = j.encode('utf-8')
31
73
 
32
74
  f.write(struct.pack('<I', len(d)))
@@ -34,33 +76,74 @@ def _send_obj(f: ta.IO, o: ta.Any) -> None:
34
76
  f.flush()
35
77
 
36
78
 
37
- def _recv_obj(f: ta.IO, ty: type) -> ta.Any:
79
+ def _recv_obj(f: ta.IO, ty: ta.Any) -> ta.Any:
38
80
  d = f.read(4)
39
81
  if not d:
40
82
  return None
41
83
  if len(d) != 4:
42
- raise Exception
84
+ raise EOFError
43
85
 
44
86
  sz = struct.unpack('<I', d)[0]
45
87
  d = f.read(sz)
46
- if not d:
47
- raise Exception
88
+ if len(d) != sz:
89
+ raise EOFError
48
90
 
49
91
  j = json.loads(d.decode('utf-8'))
50
92
  return unmarshal_obj(j, ty)
51
93
 
52
94
 
95
+ ##
96
+
97
+
53
98
  def _remote_main() -> None:
54
99
  rt = pyremote_bootstrap_finalize() # noqa
55
100
 
56
101
  while True:
57
- i = _recv_obj(rt.input, SubprocessCommand.Input)
102
+ i = _recv_obj(rt.input, Command.Input)
58
103
  if i is None:
59
104
  break
60
105
 
61
- o = SubprocessCommand()._execute(i) # noqa
106
+ if isinstance(i, SubprocessCommand.Input):
107
+ o = SubprocessCommand()._execute(i) # noqa
108
+ else:
109
+ raise TypeError(i)
110
+
111
+ _send_obj(rt.output, o, Command.Output)
112
+
113
+
114
+ ##
115
+
116
+
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())
62
132
 
63
- _send_obj(rt.output, o)
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.read_text(__package__, '_manage.py')
144
+
145
+
146
+ ##
64
147
 
65
148
 
66
149
  def _main() -> None:
@@ -77,23 +160,7 @@ def _main() -> None:
77
160
 
78
161
  #
79
162
 
80
- self_src = inspect.getsource(sys.modules[__name__])
81
- self_src_lines = self_src.splitlines()
82
- for l in self_src_lines:
83
- if l.startswith('# @omlish-amalg-output '):
84
- is_self_amalg = True
85
- break
86
- else:
87
- is_self_amalg = False
88
-
89
- if is_self_amalg:
90
- amalg_src = self_src
91
- else:
92
- amalg_file = args._amalg_file # noqa
93
- if amalg_file is None:
94
- amalg_file = os.path.join(os.path.dirname(__file__), '_manage.py')
95
- with open(amalg_file) as f:
96
- amalg_src = f.read()
163
+ amalg_src = _get_amalg_src(amalg_file=args._amalg_file) # noqa
97
164
 
98
165
  #
99
166
 
@@ -128,8 +195,13 @@ def _main() -> None:
128
195
  stdin = check_not_none(proc.stdin)
129
196
  stdout = check_not_none(proc.stdout)
130
197
 
131
- res = PyremoteBootstrapDriver(remote_src).run(stdin, stdout)
132
- print(res)
198
+ res = PyremoteBootstrapDriver( # noqa
199
+ remote_src,
200
+ PyremoteBootstrapOptions(
201
+ # debug=True,
202
+ ),
203
+ ).run(stdin, stdout)
204
+ # print(res)
133
205
 
134
206
  #
135
207
 
@@ -144,9 +216,9 @@ def _main() -> None:
144
216
  capture_stdout=True,
145
217
  ),
146
218
  ]:
147
- _send_obj(stdin, ci)
219
+ _send_obj(stdin, ci, Command.Input)
148
220
 
149
- o = _recv_obj(stdout, SubprocessCommand.Output)
221
+ o = _recv_obj(stdout, Command.Output)
150
222
 
151
223
  print(o)
152
224
 
ominfra/pyremote.py CHANGED
@@ -5,7 +5,6 @@ Basically this: https://mitogen.networkgenomics.com/howitworks.html
5
5
  """
6
6
  import base64
7
7
  import dataclasses as dc
8
- import inspect
9
8
  import json
10
9
  import os
11
10
  import platform
@@ -13,7 +12,6 @@ import pwd
13
12
  import site
14
13
  import struct
15
14
  import sys
16
- import textwrap
17
15
  import typing as ta
18
16
  import zlib
19
17
 
@@ -21,11 +19,120 @@ import zlib
21
19
  ##
22
20
 
23
21
 
22
+ @dc.dataclass(frozen=True)
23
+ class PyremoteBootstrapOptions:
24
+ debug: bool = False
25
+
26
+
27
+ ##
28
+
29
+
30
+ @dc.dataclass(frozen=True)
31
+ class PyremoteEnvInfo:
32
+ sys_base_prefix: str
33
+ sys_byteorder: str
34
+ sys_defaultencoding: str
35
+ sys_exec_prefix: str
36
+ sys_executable: str
37
+ sys_implementation_name: str
38
+ sys_path: ta.List[str]
39
+ sys_platform: str
40
+ sys_prefix: str
41
+ sys_version: str
42
+ sys_version_info: ta.List[ta.Union[int, str]]
43
+
44
+ platform_architecture: ta.List[str]
45
+ platform_machine: str
46
+ platform_platform: str
47
+ platform_processor: str
48
+ platform_system: str
49
+ platform_release: str
50
+ platform_version: str
51
+
52
+ site_userbase: str
53
+
54
+ os_cwd: str
55
+ os_gid: int
56
+ os_loadavg: ta.List[float]
57
+ os_login: ta.Optional[str]
58
+ os_pgrp: int
59
+ os_pid: int
60
+ os_ppid: int
61
+ os_uid: int
62
+
63
+ pw_name: str
64
+ pw_uid: int
65
+ pw_gid: int
66
+ pw_gecos: str
67
+ pw_dir: str
68
+ pw_shell: str
69
+
70
+ env_path: ta.Optional[str]
71
+
72
+
73
+ def _get_pyremote_env_info() -> PyremoteEnvInfo:
74
+ os_uid = os.getuid()
75
+
76
+ pw = pwd.getpwuid(os_uid)
77
+
78
+ os_login: ta.Optional[str]
79
+ try:
80
+ os_login = os.getlogin()
81
+ except OSError:
82
+ os_login = None
83
+
84
+ return PyremoteEnvInfo(
85
+ sys_base_prefix=sys.base_prefix,
86
+ sys_byteorder=sys.byteorder,
87
+ sys_defaultencoding=sys.getdefaultencoding(),
88
+ sys_exec_prefix=sys.exec_prefix,
89
+ sys_executable=sys.executable,
90
+ sys_implementation_name=sys.implementation.name,
91
+ sys_path=sys.path,
92
+ sys_platform=sys.platform,
93
+ sys_prefix=sys.prefix,
94
+ sys_version=sys.version,
95
+ sys_version_info=list(sys.version_info),
96
+
97
+ platform_architecture=list(platform.architecture()),
98
+ platform_machine=platform.machine(),
99
+ platform_platform=platform.platform(),
100
+ platform_processor=platform.processor(),
101
+ platform_system=platform.system(),
102
+ platform_release=platform.release(),
103
+ platform_version=platform.version(),
104
+
105
+ site_userbase=site.getuserbase(),
106
+
107
+ os_cwd=os.getcwd(),
108
+ os_gid=os.getgid(),
109
+ os_loadavg=list(os.getloadavg()),
110
+ os_login=os_login,
111
+ os_pgrp=os.getpgrp(),
112
+ os_pid=os.getpid(),
113
+ os_ppid=os.getppid(),
114
+ os_uid=os_uid,
115
+
116
+ pw_name=pw.pw_name,
117
+ pw_uid=pw.pw_uid,
118
+ pw_gid=pw.pw_gid,
119
+ pw_gecos=pw.pw_gecos,
120
+ pw_dir=pw.pw_dir,
121
+ pw_shell=pw.pw_shell,
122
+
123
+ env_path=os.environ.get('PATH'),
124
+ )
125
+
126
+
127
+ ##
128
+
129
+
24
130
  _PYREMOTE_BOOTSTRAP_INPUT_FD = 100
25
131
  _PYREMOTE_BOOTSTRAP_SRC_FD = 101
26
132
 
27
133
  _PYREMOTE_BOOTSTRAP_CHILD_PID_VAR = '_OPYR_CHILD_PID'
28
134
  _PYREMOTE_BOOTSTRAP_ARGV0_VAR = '_OPYR_ARGV0'
135
+ _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR = '_OPYR_OPTIONS_JSON'
29
136
 
30
137
  _PYREMOTE_BOOTSTRAP_ACK0 = b'OPYR000\n'
31
138
  _PYREMOTE_BOOTSTRAP_ACK1 = b'OPYR001\n'
@@ -87,7 +194,9 @@ def _pyremote_bootstrap_main(context_name: str) -> None:
87
194
 
88
195
  # Read main src from stdin
89
196
  main_z_len = struct.unpack('<I', os.read(0, 4))[0]
90
- main_src = zlib.decompress(os.fdopen(0, 'rb').read(main_z_len))
197
+ if len(main_z := os.fdopen(0, 'rb').read(main_z_len)) != main_z_len:
198
+ raise EOFError
199
+ main_src = zlib.decompress(main_z)
91
200
 
92
201
  # Write both copies of main src. Must write to w0 (parent stdin) before w1 (copy pipe) as pipe will likely fill
93
202
  # and block and need to be drained by pyremote_bootstrap_finalize running in parent.
@@ -110,6 +219,8 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
110
219
  if any(c in context_name for c in '\'"'):
111
220
  raise NameError(context_name)
112
221
 
222
+ import inspect
223
+ import textwrap
113
224
  bs_src = textwrap.dedent(inspect.getsource(_pyremote_bootstrap_main))
114
225
 
115
226
  for gl in [
@@ -136,9 +247,6 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
136
247
  bs_z = zlib.compress(bs_src.encode('utf-8'))
137
248
  bs_z64 = base64.encodebytes(bs_z).replace(b'\n', b'')
138
249
 
139
- def dq_repr(o: ta.Any) -> str:
140
- return '"' + repr(o)[1:-1] + '"'
141
-
142
250
  stmts = [
143
251
  f'import {", ".join(_PYREMOTE_BOOTSTRAP_IMPORTS)}',
144
252
  f'exec(zlib.decompress(base64.decodebytes({bs_z64!r})))',
@@ -153,99 +261,83 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
153
261
 
154
262
 
155
263
  @dc.dataclass(frozen=True)
156
- class PyremoteEnvInfo:
157
- sys_base_prefix: str
158
- sys_byteorder: str
159
- sys_defaultencoding: str
160
- sys_exec_prefix: str
161
- sys_executable: str
162
- sys_implementation_name: str
163
- sys_path: ta.List[str]
164
- sys_platform: str
165
- sys_prefix: str
166
- sys_version: str
167
- sys_version_info: ta.List[ta.Union[int, str]]
168
-
169
- platform_architecture: ta.List[str]
170
- platform_machine: str
171
- platform_platform: str
172
- platform_processor: str
173
- platform_system: str
174
- platform_release: str
175
- platform_version: str
176
-
177
- site_userbase: str
178
-
179
- os_cwd: str
180
- os_gid: int
181
- os_loadavg: ta.List[float]
182
- os_login: ta.Optional[str]
183
- os_pgrp: int
184
- os_pid: int
185
- os_ppid: int
186
- os_uid: int
187
-
188
- pw_name: str
189
- pw_uid: int
190
- pw_gid: int
191
- pw_gecos: str
192
- pw_dir: str
193
- pw_shell: str
194
-
195
- env_path: ta.Optional[str]
264
+ class PyremotePayloadRuntime:
265
+ input: ta.BinaryIO
266
+ output: ta.BinaryIO
267
+ main_src: str
268
+ options: PyremoteBootstrapOptions
269
+ env_info: PyremoteEnvInfo
196
270
 
197
271
 
198
- def _get_pyremote_env_info() -> PyremoteEnvInfo:
199
- os_uid = os.getuid()
272
+ 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:
275
+ # Read second copy of main src
276
+ r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
277
+ main_src = r1.read().decode('utf-8')
278
+ r1.close()
279
+
280
+ # Reap boostrap child. Must be done after reading second copy of source because source may be too big to fit in
281
+ # a pipe at once.
282
+ os.waitpid(int(os.environ.pop(_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR)), 0)
283
+
284
+ # Read options
285
+ options_json_len = struct.unpack('<I', os.read(_PYREMOTE_BOOTSTRAP_INPUT_FD, 4))[0]
286
+ if len(options_json := os.read(_PYREMOTE_BOOTSTRAP_INPUT_FD, options_json_len)) != options_json_len:
287
+ raise EOFError
288
+ options = PyremoteBootstrapOptions(**json.loads(options_json.decode('utf-8')))
289
+
290
+ # If debugging, re-exec as file
291
+ if options.debug:
292
+ # Write temp source file
293
+ import tempfile
294
+ tfd, tfn = tempfile.mkstemp('-pyremote.py')
295
+ os.write(tfd, main_src.encode('utf-8'))
296
+ os.close(tfd)
297
+
298
+ # Set json options var
299
+ os.environ[_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR] = options_json.decode('utf-8')
300
+
301
+ # Re-exec temp file
302
+ os.execl(os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR], sys.orig_argv[0], tfn)
200
303
 
201
- pw = pwd.getpwuid(os_uid)
304
+ else:
305
+ # Load options json var
306
+ options_json_str = os.environ.pop(_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR)
307
+ options = PyremoteBootstrapOptions(**json.loads(options_json_str))
202
308
 
203
- os_login: ta.Optional[str]
204
- try:
205
- os_login = os.getlogin()
206
- except OSError:
207
- os_login = None
309
+ # Read temp source file
310
+ with open(sys.orig_argv[1]) as sf:
311
+ main_src = sf.read()
208
312
 
209
- return PyremoteEnvInfo(
210
- sys_base_prefix=sys.base_prefix,
211
- sys_byteorder=sys.byteorder,
212
- sys_defaultencoding=sys.getdefaultencoding(),
213
- sys_exec_prefix=sys.exec_prefix,
214
- sys_executable=sys.executable,
215
- sys_implementation_name=sys.implementation.name,
216
- sys_path=sys.path,
217
- sys_platform=sys.platform,
218
- sys_prefix=sys.prefix,
219
- sys_version=sys.version,
220
- sys_version_info=list(sys.version_info),
313
+ # Restore original argv0
314
+ sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
221
315
 
222
- platform_architecture=list(platform.architecture()),
223
- platform_machine=platform.machine(),
224
- platform_platform=platform.platform(),
225
- platform_processor=platform.processor(),
226
- platform_system=platform.system(),
227
- platform_release=platform.release(),
228
- platform_version=platform.version(),
316
+ # Write third ack
317
+ os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
229
318
 
230
- site_userbase=site.getuserbase(),
319
+ # Write env info
320
+ env_info = _get_pyremote_env_info()
321
+ env_info_json = json.dumps(dc.asdict(env_info), indent=None, separators=(',', ':')) # noqa
322
+ os.write(1, struct.pack('<I', len(env_info_json)))
323
+ os.write(1, env_info_json.encode('utf-8'))
231
324
 
232
- os_cwd=os.getcwd(),
233
- os_gid=os.getgid(),
234
- os_loadavg=list(os.getloadavg()),
235
- os_login=os_login,
236
- os_pgrp=os.getpgrp(),
237
- os_pid=os.getpid(),
238
- os_ppid=os.getppid(),
239
- os_uid=os_uid,
325
+ # Setup IO
326
+ input = os.fdopen(_PYREMOTE_BOOTSTRAP_INPUT_FD, 'rb', 0) # noqa
327
+ output = os.fdopen(os.dup(1), 'wb', 0) # noqa
328
+ os.dup2(nfd := os.open('/dev/null', os.O_WRONLY), 1)
329
+ os.close(nfd)
240
330
 
241
- pw_name=pw.pw_name,
242
- pw_uid=pw.pw_uid,
243
- pw_gid=pw.pw_gid,
244
- pw_gecos=pw.pw_gecos,
245
- pw_dir=pw.pw_dir,
246
- pw_shell=pw.pw_shell,
331
+ # Write fourth ack
332
+ output.write(_PYREMOTE_BOOTSTRAP_ACK3)
247
333
 
248
- env_path=os.environ.get('PATH'),
334
+ # Return
335
+ return PyremotePayloadRuntime(
336
+ input=input,
337
+ output=output,
338
+ main_src=main_src,
339
+ options=options,
340
+ env_info=env_info,
249
341
  )
250
342
 
251
343
 
@@ -253,12 +345,15 @@ def _get_pyremote_env_info() -> PyremoteEnvInfo:
253
345
 
254
346
 
255
347
  class PyremoteBootstrapDriver:
256
- def __init__(self, main_src: str) -> None:
348
+ def __init__(self, main_src: str, options: PyremoteBootstrapOptions = PyremoteBootstrapOptions()) -> None:
257
349
  super().__init__()
258
350
 
259
351
  self._main_src = main_src
260
352
  self._main_z = zlib.compress(main_src.encode('utf-8'))
261
353
 
354
+ self._options = options
355
+ self._options_json = json.dumps(dc.asdict(options), indent=None, separators=(',', ':')).encode('utf-8') # noqa
356
+
262
357
  #
263
358
 
264
359
  @dc.dataclass(frozen=True)
@@ -278,7 +373,7 @@ class PyremoteBootstrapDriver:
278
373
  env_info: PyremoteEnvInfo
279
374
 
280
375
  def gen(self) -> ta.Generator[ta.Union[Read, Write], ta.Optional[bytes], Result]:
281
- # Read first ack
376
+ # Read first ack (after fork)
282
377
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK0)
283
378
 
284
379
  # Read pid
@@ -289,8 +384,14 @@ class PyremoteBootstrapDriver:
289
384
  yield from self._write(struct.pack('<I', len(self._main_z)))
290
385
  yield from self._write(self._main_z)
291
386
 
292
- # Read second and third ack
387
+ # Read second ack (after writing src copies)
293
388
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK1)
389
+
390
+ # Write options
391
+ yield from self._write(struct.pack('<I', len(self._options_json)))
392
+ yield from self._write(self._options_json)
393
+
394
+ # Read third ack (after reaping child process)
294
395
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK2)
295
396
 
296
397
  # Read env info
@@ -300,7 +401,7 @@ class PyremoteBootstrapDriver:
300
401
  env_info_json = d.decode('utf-8')
301
402
  env_info = PyremoteEnvInfo(**json.loads(env_info_json))
302
403
 
303
- # Read fourth ack
404
+ # Read fourth ack (after finalization completed)
304
405
  yield from self._expect(_PYREMOTE_BOOTSTRAP_ACK3)
305
406
 
306
407
  # Return
@@ -343,61 +444,11 @@ class PyremoteBootstrapDriver:
343
444
  return e.value
344
445
 
345
446
  if isinstance(go, self.Read):
346
- gi = stdout.read(go.sz)
447
+ if len(gi := stdout.read(go.sz)) != go.sz:
448
+ raise EOFError
347
449
  elif isinstance(go, self.Write):
348
450
  gi = None
349
451
  stdin.write(go.d)
350
452
  stdin.flush()
351
453
  else:
352
454
  raise TypeError(go)
353
-
354
-
355
- ##
356
-
357
-
358
- @dc.dataclass(frozen=True)
359
- class PyremotePayloadRuntime:
360
- input: ta.BinaryIO
361
- output: ta.BinaryIO
362
- main_src: str
363
- env_info: PyremoteEnvInfo
364
-
365
-
366
- def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
367
- # Restore original argv0
368
- sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
369
-
370
- # Read second copy of main src
371
- r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
372
- main_src = r1.read().decode('utf-8')
373
- r1.close()
374
-
375
- # Reap boostrap child. Must be done after reading second copy of source because source may be too big to fit in a
376
- # pipe at once.
377
- os.waitpid(int(os.environ.pop(_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR)), 0)
378
-
379
- # Write third ack
380
- os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
381
-
382
- # Write env info
383
- env_info = _get_pyremote_env_info()
384
- env_info_json = json.dumps(dc.asdict(env_info), indent=None, separators=(',', ':')) # noqa
385
- os.write(1, struct.pack('<I', len(env_info_json)))
386
- os.write(1, env_info_json.encode('utf-8'))
387
-
388
- # Setup IO
389
- input = os.fdopen(_PYREMOTE_BOOTSTRAP_INPUT_FD, 'rb', 0) # noqa
390
- output = os.fdopen(os.dup(1), 'wb', 0) # noqa
391
- os.dup2(nfd := os.open('/dev/null', os.O_WRONLY), 1)
392
- os.close(nfd)
393
-
394
- # Write fourth ack
395
- output.write(_PYREMOTE_BOOTSTRAP_ACK3)
396
-
397
- # Return
398
- return PyremotePayloadRuntime(
399
- input=input,
400
- output=output,
401
- main_src=main_src,
402
- env_info=env_info,
403
- )