ominfra 0.0.0.dev138__py3-none-any.whl → 0.0.0.dev140__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. ominfra/manage/__init__.py +13 -0
  2. ominfra/manage/{new/commands → commands}/base.py +9 -7
  3. ominfra/manage/{new/commands → commands}/subprocess.py +20 -15
  4. ominfra/manage/main.py +175 -0
  5. ominfra/manage/payload.py +35 -0
  6. ominfra/manage/spawning.py +100 -0
  7. ominfra/pyremote.py +18 -8
  8. ominfra/scripts/journald2aws.py +7 -0
  9. ominfra/{manage/new/_manage.py → scripts/manage.py} +248 -153
  10. ominfra/scripts/supervisor.py +7 -0
  11. {ominfra-0.0.0.dev138.dist-info → ominfra-0.0.0.dev140.dist-info}/METADATA +3 -3
  12. {ominfra-0.0.0.dev138.dist-info → ominfra-0.0.0.dev140.dist-info}/RECORD +17 -44
  13. ominfra/manage/deploy/_executor.py +0 -1415
  14. ominfra/manage/deploy/configs.py +0 -19
  15. ominfra/manage/deploy/executor/__init__.py +0 -1
  16. ominfra/manage/deploy/executor/base.py +0 -115
  17. ominfra/manage/deploy/executor/concerns/__init__.py +0 -0
  18. ominfra/manage/deploy/executor/concerns/dirs.py +0 -28
  19. ominfra/manage/deploy/executor/concerns/nginx.py +0 -47
  20. ominfra/manage/deploy/executor/concerns/repo.py +0 -17
  21. ominfra/manage/deploy/executor/concerns/supervisor.py +0 -46
  22. ominfra/manage/deploy/executor/concerns/systemd.py +0 -88
  23. ominfra/manage/deploy/executor/concerns/user.py +0 -25
  24. ominfra/manage/deploy/executor/concerns/venv.py +0 -22
  25. ominfra/manage/deploy/executor/main.py +0 -119
  26. ominfra/manage/deploy/poly/__init__.py +0 -1
  27. ominfra/manage/deploy/poly/_main.py +0 -975
  28. ominfra/manage/deploy/poly/base.py +0 -178
  29. ominfra/manage/deploy/poly/configs.py +0 -38
  30. ominfra/manage/deploy/poly/deploy.py +0 -25
  31. ominfra/manage/deploy/poly/main.py +0 -18
  32. ominfra/manage/deploy/poly/nginx.py +0 -60
  33. ominfra/manage/deploy/poly/repo.py +0 -41
  34. ominfra/manage/deploy/poly/runtime.py +0 -39
  35. ominfra/manage/deploy/poly/site.py +0 -11
  36. ominfra/manage/deploy/poly/supervisor.py +0 -64
  37. ominfra/manage/deploy/poly/venv.py +0 -52
  38. ominfra/manage/deploy/remote.py +0 -91
  39. ominfra/manage/manage.py +0 -12
  40. ominfra/manage/new/__init__.py +0 -1
  41. ominfra/manage/new/commands/__init__.py +0 -0
  42. ominfra/manage/new/main.py +0 -234
  43. /ominfra/manage/{deploy → commands}/__init__.py +0 -0
  44. {ominfra-0.0.0.dev138.dist-info → ominfra-0.0.0.dev140.dist-info}/LICENSE +0 -0
  45. {ominfra-0.0.0.dev138.dist-info → ominfra-0.0.0.dev140.dist-info}/WHEEL +0 -0
  46. {ominfra-0.0.0.dev138.dist-info → ominfra-0.0.0.dev140.dist-info}/entry_points.txt +0 -0
  47. {ominfra-0.0.0.dev138.dist-info → ominfra-0.0.0.dev140.dist-info}/top_level.txt +0 -0
@@ -2,11 +2,11 @@
2
2
  # noinspection DuplicatedCode
3
3
  # @omlish-lite
4
4
  # @omlish-script
5
- # @omlish-amalg-output main.py
5
+ # @omlish-amalg-output ../manage/main.py
6
6
  # ruff: noqa: N802 UP006 UP007 UP036
7
7
  """
8
8
  manage.py -s 'docker run -i python:3.12'
9
- manage.py -qs 'ssh -i foo/bar foo@bar.baz' --python=python3.8
9
+ manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
10
10
  """
11
11
  import abc
12
12
  import base64
@@ -49,13 +49,14 @@ if sys.version_info < (3, 8):
49
49
 
50
50
 
51
51
  # commands/base.py
52
- CommandInputT = ta.TypeVar('CommandInputT', bound='Command.Input')
52
+ CommandT = ta.TypeVar('CommandT', bound='Command')
53
53
  CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
54
54
 
55
- # ../../../omlish/lite/cached.py
55
+ # ../../omlish/lite/cached.py
56
56
  T = ta.TypeVar('T')
57
+ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
57
58
 
58
- # ../../../omlish/lite/check.py
59
+ # ../../omlish/lite/check.py
59
60
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
60
61
 
61
62
 
@@ -66,22 +67,24 @@ SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
66
67
  ##
67
68
 
68
69
 
69
- class Command(abc.ABC, ta.Generic[CommandInputT, CommandOutputT]):
70
- @dc.dataclass(frozen=True)
71
- class Input(abc.ABC): # noqa
72
- pass
73
-
70
+ @dc.dataclass(frozen=True)
71
+ class Command(abc.ABC, ta.Generic[CommandOutputT]):
74
72
  @dc.dataclass(frozen=True)
75
73
  class Output(abc.ABC): # noqa
76
74
  pass
77
75
 
76
+
77
+ ##
78
+
79
+
80
+ class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
78
81
  @abc.abstractmethod
79
- def _execute(self, inp: CommandInputT) -> CommandOutputT:
82
+ def execute(self, i: CommandT) -> CommandOutputT:
80
83
  raise NotImplementedError
81
84
 
82
85
 
83
86
  ########################################
84
- # ../../../pyremote.py
87
+ # ../../pyremote.py
85
88
  """
86
89
  Basically this: https://mitogen.networkgenomics.com/howitworks.html
87
90
  """
@@ -203,6 +206,8 @@ _PYREMOTE_BOOTSTRAP_SRC_FD = 101
203
206
 
204
207
  _PYREMOTE_BOOTSTRAP_CHILD_PID_VAR = '_OPYR_CHILD_PID'
205
208
  _PYREMOTE_BOOTSTRAP_ARGV0_VAR = '_OPYR_ARGV0'
209
+ _PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR = '_OPYR_CONTEXT_NAME'
210
+ _PYREMOTE_BOOTSTRAP_SRC_FILE_VAR = '_OPYR_SRC_FILE'
206
211
  _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR = '_OPYR_OPTIONS_JSON'
207
212
 
208
213
  _PYREMOTE_BOOTSTRAP_ACK0 = b'OPYR000\n'
@@ -245,11 +250,10 @@ def _pyremote_bootstrap_main(context_name: str) -> None:
245
250
  for f in [r0, w0, r1, w1]:
246
251
  os.close(f)
247
252
 
248
- # Save child pid to close after relaunch
253
+ # Save vars
249
254
  os.environ[_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR] = str(cp)
250
-
251
- # Save original argv0
252
255
  os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR] = sys.executable
256
+ os.environ[_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR] = context_name
253
257
 
254
258
  # Start repl reading stdin from r0
255
259
  os.execl(sys.executable, sys.executable + (_PYREMOTE_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)))
@@ -300,6 +304,7 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
300
304
 
301
305
  '_PYREMOTE_BOOTSTRAP_CHILD_PID_VAR',
302
306
  '_PYREMOTE_BOOTSTRAP_ARGV0_VAR',
307
+ '_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR',
303
308
 
304
309
  '_PYREMOTE_BOOTSTRAP_ACK0',
305
310
  '_PYREMOTE_BOOTSTRAP_ACK1',
@@ -335,14 +340,15 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
335
340
  class PyremotePayloadRuntime:
336
341
  input: ta.BinaryIO
337
342
  output: ta.BinaryIO
343
+ context_name: str
338
344
  main_src: str
339
345
  options: PyremoteBootstrapOptions
340
346
  env_info: PyremoteEnvInfo
341
347
 
342
348
 
343
349
  def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
344
- # If json options var is not present we need to do initial finalization
345
- if _PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR not in os.environ:
350
+ # If src file var is not present we need to do initial finalization
351
+ if _PYREMOTE_BOOTSTRAP_SRC_FILE_VAR not in os.environ:
346
352
  # Read second copy of main src
347
353
  r1 = os.fdopen(_PYREMOTE_BOOTSTRAP_SRC_FD, 'rb', 0)
348
354
  main_src = r1.read().decode('utf-8')
@@ -366,11 +372,14 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
366
372
  os.write(tfd, main_src.encode('utf-8'))
367
373
  os.close(tfd)
368
374
 
369
- # Set json options var
375
+ # Set vars
376
+ os.environ[_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR] = tfn
370
377
  os.environ[_PYREMOTE_BOOTSTRAP_OPTIONS_JSON_VAR] = options_json.decode('utf-8')
371
378
 
372
379
  # Re-exec temp file
373
- os.execl(os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR], sys.orig_argv[0], tfn)
380
+ exe = os.environ[_PYREMOTE_BOOTSTRAP_ARGV0_VAR]
381
+ context_name = os.environ[_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR]
382
+ os.execl(exe, exe + (_PYREMOTE_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)), tfn)
374
383
 
375
384
  else:
376
385
  # Load options json var
@@ -378,12 +387,15 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
378
387
  options = PyremoteBootstrapOptions(**json.loads(options_json_str))
379
388
 
380
389
  # Read temp source file
381
- with open(sys.orig_argv[1]) as sf:
390
+ with open(os.environ.pop(_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR)) as sf:
382
391
  main_src = sf.read()
383
392
 
384
393
  # Restore original argv0
385
394
  sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
386
395
 
396
+ # Grab context name
397
+ context_name = os.environ.pop(_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR)
398
+
387
399
  # Write third ack
388
400
  os.write(1, _PYREMOTE_BOOTSTRAP_ACK2)
389
401
 
@@ -406,6 +418,7 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
406
418
  return PyremotePayloadRuntime(
407
419
  input=input,
408
420
  output=output,
421
+ context_name=context_name,
409
422
  main_src=main_src,
410
423
  options=options,
411
424
  env_info=env_info,
@@ -526,7 +539,7 @@ class PyremoteBootstrapDriver:
526
539
 
527
540
 
528
541
  ########################################
529
- # ../../../../omlish/lite/cached.py
542
+ # ../../../omlish/lite/cached.py
530
543
 
531
544
 
532
545
  class _cached_nullary: # noqa
@@ -550,8 +563,14 @@ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
550
563
  return _cached_nullary(fn)
551
564
 
552
565
 
566
+ def static_init(fn: CallableT) -> CallableT:
567
+ fn = cached_nullary(fn)
568
+ fn()
569
+ return fn
570
+
571
+
553
572
  ########################################
554
- # ../../../../omlish/lite/check.py
573
+ # ../../../omlish/lite/check.py
555
574
 
556
575
 
557
576
  def check_isinstance(v: ta.Any, spec: ta.Union[ta.Type[T], tuple]) -> T:
@@ -648,7 +667,7 @@ def check_non_empty(v: SizedT) -> SizedT:
648
667
 
649
668
 
650
669
  ########################################
651
- # ../../../../omlish/lite/json.py
670
+ # ../../../omlish/lite/json.py
652
671
 
653
672
 
654
673
  ##
@@ -679,7 +698,7 @@ json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON
679
698
 
680
699
 
681
700
  ########################################
682
- # ../../../../omlish/lite/reflect.py
701
+ # ../../../omlish/lite/reflect.py
683
702
 
684
703
 
685
704
  _GENERIC_ALIAS_TYPES = (
@@ -734,7 +753,40 @@ def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
734
753
 
735
754
 
736
755
  ########################################
737
- # ../../../../omlish/lite/logs.py
756
+ # ../payload.py
757
+
758
+
759
+ @cached_nullary
760
+ def _get_self_src() -> str:
761
+ return inspect.getsource(sys.modules[__name__])
762
+
763
+
764
+ def _is_src_amalg(src: str) -> bool:
765
+ for l in src.splitlines(): # noqa
766
+ if l.startswith('# @omlish-amalg-output '):
767
+ return True
768
+ return False
769
+
770
+
771
+ @cached_nullary
772
+ def _is_self_amalg() -> bool:
773
+ return _is_src_amalg(_get_self_src())
774
+
775
+
776
+ def get_payload_src(*, file: ta.Optional[str]) -> str:
777
+ if file is not None:
778
+ with open(file) as f:
779
+ return f.read()
780
+
781
+ if _is_self_amalg():
782
+ return _get_self_src()
783
+
784
+ import importlib.resources
785
+ return importlib.resources.files(__package__.split('.')[0] + '.scripts').joinpath('manage.py').read_text()
786
+
787
+
788
+ ########################################
789
+ # ../../../omlish/lite/logs.py
738
790
  """
739
791
  TODO:
740
792
  - translate json keys
@@ -1004,7 +1056,7 @@ def configure_standard_logging(
1004
1056
 
1005
1057
 
1006
1058
  ########################################
1007
- # ../../../../omlish/lite/marshal.py
1059
+ # ../../../omlish/lite/marshal.py
1008
1060
  """
1009
1061
  TODO:
1010
1062
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
@@ -1351,7 +1403,7 @@ def unmarshal_obj(o: ta.Any, ty: ta.Union[ta.Type[T], ta.Any]) -> T:
1351
1403
 
1352
1404
 
1353
1405
  ########################################
1354
- # ../../../../omlish/lite/runtime.py
1406
+ # ../../../omlish/lite/runtime.py
1355
1407
 
1356
1408
 
1357
1409
  @cached_nullary
@@ -1368,7 +1420,7 @@ def check_runtime_version() -> None:
1368
1420
 
1369
1421
 
1370
1422
  ########################################
1371
- # ../../../../omlish/lite/subprocesses.py
1423
+ # ../../../omlish/lite/subprocesses.py
1372
1424
 
1373
1425
 
1374
1426
  ##
@@ -1498,24 +1550,23 @@ def subprocess_close(
1498
1550
  ##
1499
1551
 
1500
1552
 
1501
- class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Output']):
1502
- @dc.dataclass(frozen=True)
1503
- class Input(Command.Input):
1504
- args: ta.Sequence[str]
1553
+ @dc.dataclass(frozen=True)
1554
+ class SubprocessCommand(Command['SubprocessCommand.Output']):
1555
+ args: ta.Sequence[str]
1505
1556
 
1506
- shell: bool = False
1507
- cwd: ta.Optional[str] = None
1508
- env: ta.Optional[ta.Mapping[str, str]] = None
1557
+ shell: bool = False
1558
+ cwd: ta.Optional[str] = None
1559
+ env: ta.Optional[ta.Mapping[str, str]] = None
1509
1560
 
1510
- capture_stdout: bool = False
1511
- capture_stderr: bool = False
1561
+ capture_stdout: bool = False
1562
+ capture_stderr: bool = False
1512
1563
 
1513
- input: ta.Optional[bytes] = None
1514
- timeout: ta.Optional[float] = None
1564
+ input: ta.Optional[bytes] = None
1565
+ timeout: ta.Optional[float] = None
1515
1566
 
1516
- def __post_init__(self) -> None:
1517
- if isinstance(self.args, str):
1518
- raise TypeError(self.args)
1567
+ def __post_init__(self) -> None:
1568
+ if isinstance(self.args, str):
1569
+ raise TypeError(self.args)
1519
1570
 
1520
1571
  @dc.dataclass(frozen=True)
1521
1572
  class Output(Command.Output):
@@ -1527,7 +1578,12 @@ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Ou
1527
1578
  stdout: ta.Optional[bytes] = None
1528
1579
  stderr: ta.Optional[bytes] = None
1529
1580
 
1530
- def _execute(self, inp: Input) -> Output:
1581
+
1582
+ ##
1583
+
1584
+
1585
+ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
1586
+ def execute(self, inp: SubprocessCommand) -> SubprocessCommand.Output:
1531
1587
  proc = subprocess.Popen(
1532
1588
  subprocess_maybe_shell_wrap_exec(*inp.args),
1533
1589
 
@@ -1558,6 +1614,101 @@ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Ou
1558
1614
  )
1559
1615
 
1560
1616
 
1617
+ ########################################
1618
+ # ../spawning.py
1619
+
1620
+
1621
+ class PySpawner:
1622
+ DEFAULT_PYTHON = 'python3'
1623
+
1624
+ def __init__(
1625
+ self,
1626
+ src: str,
1627
+ *,
1628
+ shell: ta.Optional[str] = None,
1629
+ shell_quote: bool = False,
1630
+ python: str = DEFAULT_PYTHON,
1631
+ stderr: ta.Optional[ta.Literal['pipe', 'stdout', 'devnull']] = None,
1632
+ ) -> None:
1633
+ super().__init__()
1634
+
1635
+ self._src = src
1636
+ self._shell = shell
1637
+ self._shell_quote = shell_quote
1638
+ self._python = python
1639
+ self._stderr = stderr
1640
+
1641
+ #
1642
+
1643
+ class _PreparedCmd(ta.NamedTuple):
1644
+ cmd: ta.Sequence[str]
1645
+ shell: bool
1646
+
1647
+ def _prepare_cmd(self) -> _PreparedCmd:
1648
+ if self._shell is not None:
1649
+ sh_src = f'{self._python} -c {shlex.quote(self._src)}'
1650
+ if self._shell_quote:
1651
+ sh_src = shlex.quote(sh_src)
1652
+ sh_cmd = f'{self._shell} {sh_src}'
1653
+ return PySpawner._PreparedCmd(
1654
+ cmd=[sh_cmd],
1655
+ shell=True,
1656
+ )
1657
+
1658
+ else:
1659
+ return PySpawner._PreparedCmd(
1660
+ cmd=[self._python, '-c', self._src],
1661
+ shell=False,
1662
+ )
1663
+
1664
+ #
1665
+
1666
+ _STDERR_KWARG_MAP: ta.Mapping[str, int] = {
1667
+ 'pipe': subprocess.PIPE,
1668
+ 'stdout': subprocess.STDOUT,
1669
+ 'devnull': subprocess.DEVNULL,
1670
+ }
1671
+
1672
+ @dc.dataclass(frozen=True)
1673
+ class Spawned:
1674
+ stdin: ta.IO
1675
+ stdout: ta.IO
1676
+ stderr: ta.Optional[ta.IO]
1677
+
1678
+ @contextlib.contextmanager
1679
+ def spawn(
1680
+ self,
1681
+ *,
1682
+ timeout: ta.Optional[float] = None,
1683
+ ) -> ta.Generator[Spawned, None, None]:
1684
+ pc = self._prepare_cmd()
1685
+
1686
+ with subprocess.Popen(
1687
+ subprocess_maybe_shell_wrap_exec(*pc.cmd),
1688
+ shell=pc.shell,
1689
+ stdin=subprocess.PIPE,
1690
+ stdout=subprocess.PIPE,
1691
+ stderr=self._STDERR_KWARG_MAP[self._stderr] if self._stderr is not None else None,
1692
+ ) as proc:
1693
+ stdin = check_not_none(proc.stdin)
1694
+ stdout = check_not_none(proc.stdout)
1695
+
1696
+ try:
1697
+ yield PySpawner.Spawned(
1698
+ stdin=stdin,
1699
+ stdout=stdout,
1700
+ stderr=proc.stderr,
1701
+ )
1702
+
1703
+ finally:
1704
+ try:
1705
+ stdin.close()
1706
+ except BrokenPipeError:
1707
+ pass
1708
+
1709
+ proc.wait(timeout)
1710
+
1711
+
1561
1712
  ########################################
1562
1713
  # main.py
1563
1714
 
@@ -1570,29 +1721,23 @@ _COMMAND_TYPES = {
1570
1721
  }
1571
1722
 
1572
1723
 
1573
- register_opj_marshaler(
1574
- Command.Input,
1575
- PolymorphicObjMarshaler.of([
1576
- PolymorphicObjMarshaler.Impl(
1577
- cty.Input,
1578
- k,
1579
- get_obj_marshaler(cty.Input),
1580
- )
1581
- for k, cty in _COMMAND_TYPES.items()
1582
- ]),
1583
- )
1584
-
1585
- register_opj_marshaler(
1586
- Command.Output,
1587
- PolymorphicObjMarshaler.of([
1588
- PolymorphicObjMarshaler.Impl(
1589
- cty.Output,
1590
- k,
1591
- get_obj_marshaler(cty.Output),
1724
+ @static_init
1725
+ def _register_command_marshaling() -> None:
1726
+ for fn in [
1727
+ lambda c: c,
1728
+ lambda c: c.Output,
1729
+ ]:
1730
+ register_opj_marshaler(
1731
+ fn(Command),
1732
+ PolymorphicObjMarshaler.of([
1733
+ PolymorphicObjMarshaler.Impl(
1734
+ fn(cty),
1735
+ k,
1736
+ get_obj_marshaler(fn(cty)),
1737
+ )
1738
+ for k, cty in _COMMAND_TYPES.items()
1739
+ ]),
1592
1740
  )
1593
- for k, cty in _COMMAND_TYPES.items()
1594
- ]),
1595
- )
1596
1741
 
1597
1742
 
1598
1743
  ##
@@ -1630,12 +1775,12 @@ def _remote_main() -> None:
1630
1775
  rt = pyremote_bootstrap_finalize() # noqa
1631
1776
 
1632
1777
  while True:
1633
- i = _recv_obj(rt.input, Command.Input)
1778
+ i = _recv_obj(rt.input, Command)
1634
1779
  if i is None:
1635
1780
  break
1636
1781
 
1637
- if isinstance(i, SubprocessCommand.Input):
1638
- o = SubprocessCommand()._execute(i) # noqa
1782
+ if isinstance(i, SubprocessCommand):
1783
+ o = SubprocessCommandExecutor().execute(i) # noqa
1639
1784
  else:
1640
1785
  raise TypeError(i)
1641
1786
 
@@ -1645,38 +1790,6 @@ def _remote_main() -> None:
1645
1790
  ##
1646
1791
 
1647
1792
 
1648
- @cached_nullary
1649
- def _get_self_src() -> str:
1650
- return inspect.getsource(sys.modules[__name__])
1651
-
1652
-
1653
- def _is_src_amalg(src: str) -> bool:
1654
- for l in src.splitlines(): # noqa
1655
- if l.startswith('# @omlish-amalg-output '):
1656
- return True
1657
- return False
1658
-
1659
-
1660
- @cached_nullary
1661
- def _is_self_amalg() -> bool:
1662
- return _is_src_amalg(_get_self_src())
1663
-
1664
-
1665
- def _get_amalg_src(*, amalg_file: ta.Optional[str]) -> str:
1666
- if amalg_file is not None:
1667
- with open(amalg_file) as f:
1668
- return f.read()
1669
-
1670
- if _is_self_amalg():
1671
- return _get_self_src()
1672
-
1673
- import importlib.resources
1674
- return importlib.resources.read_text(__package__, '_manage.py')
1675
-
1676
-
1677
- ##
1678
-
1679
-
1680
1793
  def _main() -> None:
1681
1794
  import argparse
1682
1795
 
@@ -1685,19 +1798,20 @@ def _main() -> None:
1685
1798
  parser.add_argument('-s', '--shell')
1686
1799
  parser.add_argument('-q', '--shell-quote', action='store_true')
1687
1800
  parser.add_argument('--python', default='python3')
1688
- parser.add_argument('--_amalg-file')
1801
+ parser.add_argument('--debug', action='store_true')
1802
+ parser.add_argument('--_payload-file')
1689
1803
 
1690
1804
  args = parser.parse_args()
1691
1805
 
1692
1806
  #
1693
1807
 
1694
- amalg_src = _get_amalg_src(amalg_file=args._amalg_file) # noqa
1808
+ payload_src = get_payload_src(file=args._payload_file) # noqa
1695
1809
 
1696
1810
  #
1697
1811
 
1698
1812
  remote_src = '\n\n'.join([
1699
1813
  '__name__ = "__remote__"',
1700
- amalg_src,
1814
+ payload_src,
1701
1815
  '_remote_main()',
1702
1816
  ])
1703
1817
 
@@ -1705,60 +1819,41 @@ def _main() -> None:
1705
1819
 
1706
1820
  bs_src = pyremote_build_bootstrap_cmd(__package__ or 'manage')
1707
1821
 
1708
- if args.shell is not None:
1709
- sh_src = f'{args.python} -c {shlex.quote(bs_src)}'
1710
- if args.shell_quote:
1711
- sh_src = shlex.quote(sh_src)
1712
- sh_cmd = f'{args.shell} {sh_src}'
1713
- cmd = [sh_cmd]
1714
- shell = True
1715
- else:
1716
- cmd = [args.python, '-c', bs_src]
1717
- shell = False
1718
-
1719
- proc = subprocess.Popen(
1720
- subprocess_maybe_shell_wrap_exec(*cmd),
1721
- shell=shell,
1722
- stdin=subprocess.PIPE,
1723
- stdout=subprocess.PIPE,
1724
- )
1725
-
1726
- stdin = check_not_none(proc.stdin)
1727
- stdout = check_not_none(proc.stdout)
1728
-
1729
- res = PyremoteBootstrapDriver( # noqa
1730
- remote_src,
1731
- PyremoteBootstrapOptions(
1732
- # debug=True,
1733
- ),
1734
- ).run(stdin, stdout)
1735
- # print(res)
1736
-
1737
1822
  #
1738
1823
 
1739
- for ci in [
1740
- SubprocessCommand.Input(
1741
- args=['python3', '-'],
1742
- input=b'print(1)\n',
1743
- capture_stdout=True,
1744
- ),
1745
- SubprocessCommand.Input(
1746
- args=['uname'],
1747
- capture_stdout=True,
1748
- ),
1749
- ]:
1750
- _send_obj(stdin, ci, Command.Input)
1751
-
1752
- o = _recv_obj(stdout, Command.Output)
1753
-
1754
- print(o)
1824
+ spawner = PySpawner(
1825
+ bs_src,
1826
+ shell=args.shell,
1827
+ shell_quote=args.shell_quote,
1828
+ python=args.python,
1829
+ )
1830
+ with spawner.spawn() as proc:
1831
+ res = PyremoteBootstrapDriver( # noqa
1832
+ remote_src,
1833
+ PyremoteBootstrapOptions(
1834
+ debug=args.debug,
1835
+ ),
1836
+ ).run(proc.stdin, proc.stdout)
1837
+ # print(res)
1755
1838
 
1756
- try:
1757
- stdin.close()
1758
- except BrokenPipeError:
1759
- pass
1839
+ #
1760
1840
 
1761
- proc.wait()
1841
+ for ci in [
1842
+ SubprocessCommand(
1843
+ args=['python3', '-'],
1844
+ input=b'print(1)\n',
1845
+ capture_stdout=True,
1846
+ ),
1847
+ SubprocessCommand(
1848
+ args=['uname'],
1849
+ capture_stdout=True,
1850
+ ),
1851
+ ]:
1852
+ _send_obj(proc.stdin, ci, Command)
1853
+
1854
+ o = _recv_obj(proc.stdout, Command.Output)
1855
+
1856
+ print(o)
1762
1857
 
1763
1858
 
1764
1859
  if __name__ == '__main__':
@@ -101,6 +101,7 @@ V = ta.TypeVar('V')
101
101
 
102
102
  # ../../omlish/lite/cached.py
103
103
  T = ta.TypeVar('T')
104
+ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
104
105
 
105
106
  # ../../omlish/lite/check.py
106
107
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
@@ -1454,6 +1455,12 @@ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
1454
1455
  return _cached_nullary(fn)
1455
1456
 
1456
1457
 
1458
+ def static_init(fn: CallableT) -> CallableT:
1459
+ fn = cached_nullary(fn)
1460
+ fn()
1461
+ return fn
1462
+
1463
+
1457
1464
  ########################################
1458
1465
  # ../../../omlish/lite/check.py
1459
1466
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.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"