ominfra 0.0.0.dev151__tar.gz → 0.0.0.dev152__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. {ominfra-0.0.0.dev151/ominfra.egg-info → ominfra-0.0.0.dev152}/PKG-INFO +3 -3
  2. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/journald2aws/driver.py +1 -1
  3. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/main.py +5 -7
  4. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/_main.py +1 -1
  5. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/scripts/journald2aws.py +68 -68
  6. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/scripts/manage.py +83 -45
  7. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/scripts/supervisor.py +157 -157
  8. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/main.py +1 -1
  9. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152/ominfra.egg-info}/PKG-INFO +3 -3
  10. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra.egg-info/requires.txt +2 -2
  11. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/pyproject.toml +3 -3
  12. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/LICENSE +0 -0
  13. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/MANIFEST.in +0 -0
  14. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/README.rst +0 -0
  15. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/.manifests.json +0 -0
  16. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/__about__.py +0 -0
  17. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/__init__.py +0 -0
  18. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/__init__.py +0 -0
  19. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/__init__.py +0 -0
  20. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/__main__.py +0 -0
  21. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/auth.py +0 -0
  22. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/cli.py +0 -0
  23. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/dataclasses.py +0 -0
  24. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  25. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
  26. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
  27. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/journald2aws/main.py +0 -0
  28. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
  29. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/logs.py +0 -0
  30. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/aws/metadata.py +0 -0
  31. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/gcp/__init__.py +0 -0
  32. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/clouds/gcp/auth.py +0 -0
  33. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/cmds.py +0 -0
  34. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/configs.py +0 -0
  35. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/journald/__init__.py +0 -0
  36. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/journald/fields.py +0 -0
  37. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/journald/genmessages.py +0 -0
  38. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/journald/messages.py +0 -0
  39. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/journald/tailer.py +0 -0
  40. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/__init__.py +0 -0
  41. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/__main__.py +0 -0
  42. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/bootstrap.py +0 -0
  43. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/bootstrap_.py +0 -0
  44. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/__init__.py +0 -0
  45. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/base.py +0 -0
  46. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/execution.py +0 -0
  47. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/inject.py +0 -0
  48. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/interp.py +0 -0
  49. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/marshal.py +0 -0
  50. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/commands/subprocess.py +0 -0
  51. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/config.py +0 -0
  52. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/deploy/__init__.py +0 -0
  53. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/deploy/command.py +0 -0
  54. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/deploy/inject.py +0 -0
  55. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/deploy/paths.py +0 -0
  56. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/inject.py +0 -0
  57. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/marshal.py +0 -0
  58. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/__init__.py +0 -0
  59. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/channel.py +0 -0
  60. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/config.py +0 -0
  61. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/connection.py +0 -0
  62. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/execution.py +0 -0
  63. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/inject.py +0 -0
  64. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/payload.py +0 -0
  65. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/manage/remote/spawning.py +0 -0
  66. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/pyremote.py +0 -0
  67. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/scripts/__init__.py +0 -0
  68. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/ssh.py +0 -0
  69. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/LICENSE.txt +0 -0
  70. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/__init__.py +0 -0
  71. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/__main__.py +0 -0
  72. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/configs.py +0 -0
  73. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/dispatchers.py +0 -0
  74. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/dispatchersimpl.py +0 -0
  75. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/events.py +0 -0
  76. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/exceptions.py +0 -0
  77. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/groups.py +0 -0
  78. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/groupsimpl.py +0 -0
  79. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/http.py +0 -0
  80. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/inject.py +0 -0
  81. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/io.py +0 -0
  82. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/pipes.py +0 -0
  83. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/privileges.py +0 -0
  84. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/process.py +0 -0
  85. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/processimpl.py +0 -0
  86. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/setup.py +0 -0
  87. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/setupimpl.py +0 -0
  88. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/signals.py +0 -0
  89. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/spawning.py +0 -0
  90. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/spawningimpl.py +0 -0
  91. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/states.py +0 -0
  92. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/supervisor.py +0 -0
  93. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/types.py +0 -0
  94. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/__init__.py +0 -0
  95. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/collections.py +0 -0
  96. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/diag.py +0 -0
  97. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/fds.py +0 -0
  98. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/fs.py +0 -0
  99. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/os.py +0 -0
  100. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/ostypes.py +0 -0
  101. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/signals.py +0 -0
  102. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/strings.py +0 -0
  103. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/supervisor/utils/users.py +0 -0
  104. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/tailscale/__init__.py +0 -0
  105. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/tailscale/api.py +0 -0
  106. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/tailscale/cli.py +0 -0
  107. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/threadworkers.py +0 -0
  108. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/tools/__init__.py +0 -0
  109. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra/tools/listresources.py +0 -0
  110. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra.egg-info/SOURCES.txt +0 -0
  111. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra.egg-info/dependency_links.txt +0 -0
  112. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra.egg-info/entry_points.txt +0 -0
  113. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/ominfra.egg-info/top_level.txt +0 -0
  114. {ominfra-0.0.0.dev151 → ominfra-0.0.0.dev152}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev151
3
+ Version: 0.0.0.dev152
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.dev151
16
- Requires-Dist: omlish==0.0.0.dev151
15
+ Requires-Dist: omdev==0.0.0.dev152
16
+ Requires-Dist: omlish==0.0.0.dev152
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -41,8 +41,8 @@ from omlish.lite.cached import cached_nullary
41
41
  from omlish.lite.check import check
42
42
  from omlish.lite.contextmanagers import ExitStacked
43
43
  from omlish.lite.logs import log
44
- from omlish.lite.pidfile import Pidfile
45
44
  from omlish.lite.runtime import is_debugger_attached
45
+ from omlish.os.pidfile import Pidfile
46
46
 
47
47
  from ....journald.messages import JournalctlMessage # noqa
48
48
  from ....journald.tailer import JournalctlTailerWorker
@@ -8,6 +8,7 @@ manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
8
8
  import asyncio
9
9
  import contextlib
10
10
  import json
11
+ import sys
11
12
  import typing as ta
12
13
 
13
14
  from omlish.argparse.cli import ArgparseCli
@@ -31,7 +32,7 @@ from .remote.spawning import RemoteSpawning
31
32
 
32
33
  class MainCli(ArgparseCli):
33
34
  @argparse_command(
34
- argparse_arg('--payload-file'),
35
+ argparse_arg('--_payload-file'),
35
36
 
36
37
  argparse_arg('-s', '--shell'),
37
38
  argparse_arg('-q', '--shell-quote', action='store_true'),
@@ -49,10 +50,7 @@ class MainCli(ArgparseCli):
49
50
 
50
51
  argparse_arg('command', nargs='+'),
51
52
  )
52
- def run(self) -> None:
53
- asyncio.run(self._async_run())
54
-
55
- async def _async_run(self) -> None:
53
+ async def run(self) -> None:
56
54
  bs = MainBootstrap(
57
55
  main_config=MainConfig(
58
56
  log_level='DEBUG' if self.args.debug else 'INFO',
@@ -61,7 +59,7 @@ class MainCli(ArgparseCli):
61
59
  ),
62
60
 
63
61
  remote_config=RemoteConfig(
64
- payload_file=self.args.payload_file, # noqa
62
+ payload_file=self.args._payload_file, # noqa
65
63
 
66
64
  pycharm_remote_debug=PycharmRemoteDebug(
67
65
  port=self.args.pycharm_debug_port,
@@ -124,7 +122,7 @@ class MainCli(ArgparseCli):
124
122
 
125
123
 
126
124
  def _main() -> None:
127
- MainCli().call_and_exit()
125
+ sys.exit(asyncio.run(MainCli().async_cli_run()))
128
126
 
129
127
 
130
128
  if __name__ == '__main__':
@@ -12,11 +12,11 @@ from omlish.lite.asyncio.asyncio import asyncio_open_stream_reader
12
12
  from omlish.lite.asyncio.asyncio import asyncio_open_stream_writer
13
13
  from omlish.lite.cached import cached_nullary
14
14
  from omlish.lite.check import check
15
- from omlish.lite.deathsig import set_process_deathsig
16
15
  from omlish.lite.inject import Injector
17
16
  from omlish.lite.logs import log
18
17
  from omlish.lite.marshal import ObjMarshalerManager
19
18
  from omlish.lite.pycharm import pycharm_debug_connect
19
+ from omlish.os.deathsig import set_process_deathsig
20
20
 
21
21
  from ...pyremote import pyremote_bootstrap_finalize
22
22
  from ..bootstrap import MainBootstrap
@@ -79,7 +79,7 @@ ConfigMapping = ta.Mapping[str, ta.Any]
79
79
  ThreadWorkerT = ta.TypeVar('ThreadWorkerT', bound='ThreadWorker')
80
80
 
81
81
  # ../../../../omlish/lite/subprocesses.py
82
- SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
82
+ SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
83
83
 
84
84
 
85
85
  ########################################
@@ -1439,73 +1439,6 @@ json_dump_compact: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON
1439
1439
  json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
1440
1440
 
1441
1441
 
1442
- ########################################
1443
- # ../../../../../omlish/lite/pidfile.py
1444
-
1445
-
1446
- class Pidfile:
1447
- def __init__(self, path: str) -> None:
1448
- super().__init__()
1449
- self._path = path
1450
-
1451
- _f: ta.TextIO
1452
-
1453
- def __repr__(self) -> str:
1454
- return f'{self.__class__.__name__}({self._path!r})'
1455
-
1456
- def __enter__(self) -> 'Pidfile':
1457
- fd = os.open(self._path, os.O_RDWR | os.O_CREAT, 0o600)
1458
- try:
1459
- os.set_inheritable(fd, True)
1460
- f = os.fdopen(fd, 'r+')
1461
- except Exception:
1462
- try:
1463
- os.close(fd)
1464
- except Exception: # noqa
1465
- pass
1466
- raise
1467
- self._f = f
1468
- return self
1469
-
1470
- def __exit__(self, exc_type, exc_val, exc_tb):
1471
- if hasattr(self, '_f'):
1472
- self._f.close()
1473
- del self._f
1474
-
1475
- def try_lock(self) -> bool:
1476
- try:
1477
- fcntl.flock(self._f, fcntl.LOCK_EX | fcntl.LOCK_NB)
1478
- return True
1479
- except OSError:
1480
- return False
1481
-
1482
- def ensure_locked(self) -> None:
1483
- if not self.try_lock():
1484
- raise RuntimeError('Could not get lock')
1485
-
1486
- def write(self, pid: ta.Optional[int] = None) -> None:
1487
- self.ensure_locked()
1488
- if pid is None:
1489
- pid = os.getpid()
1490
- self._f.write(f'{pid}\n')
1491
- self._f.flush()
1492
-
1493
- def clear(self) -> None:
1494
- self.ensure_locked()
1495
- self._f.seek(0)
1496
- self._f.truncate()
1497
-
1498
- def read(self) -> int:
1499
- if self.try_lock():
1500
- raise RuntimeError('Got lock')
1501
- self._f.seek(0)
1502
- return int(self._f.read())
1503
-
1504
- def kill(self, sig: int = signal.SIGTERM) -> None:
1505
- pid = self.read()
1506
- os.kill(pid, sig) # FIXME: Still racy
1507
-
1508
-
1509
1442
  ########################################
1510
1443
  # ../../../../../omlish/lite/reflect.py
1511
1444
 
@@ -1628,6 +1561,73 @@ def format_num_bytes(num_bytes: int) -> str:
1628
1561
  return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
1629
1562
 
1630
1563
 
1564
+ ########################################
1565
+ # ../../../../../omlish/os/pidfile.py
1566
+
1567
+
1568
+ class Pidfile:
1569
+ def __init__(self, path: str) -> None:
1570
+ super().__init__()
1571
+ self._path = path
1572
+
1573
+ _f: ta.TextIO
1574
+
1575
+ def __repr__(self) -> str:
1576
+ return f'{self.__class__.__name__}({self._path!r})'
1577
+
1578
+ def __enter__(self) -> 'Pidfile':
1579
+ fd = os.open(self._path, os.O_RDWR | os.O_CREAT, 0o600)
1580
+ try:
1581
+ os.set_inheritable(fd, True)
1582
+ f = os.fdopen(fd, 'r+')
1583
+ except Exception:
1584
+ try:
1585
+ os.close(fd)
1586
+ except Exception: # noqa
1587
+ pass
1588
+ raise
1589
+ self._f = f
1590
+ return self
1591
+
1592
+ def __exit__(self, exc_type, exc_val, exc_tb):
1593
+ if hasattr(self, '_f'):
1594
+ self._f.close()
1595
+ del self._f
1596
+
1597
+ def try_lock(self) -> bool:
1598
+ try:
1599
+ fcntl.flock(self._f, fcntl.LOCK_EX | fcntl.LOCK_NB)
1600
+ return True
1601
+ except OSError:
1602
+ return False
1603
+
1604
+ def ensure_locked(self) -> None:
1605
+ if not self.try_lock():
1606
+ raise RuntimeError('Could not get lock')
1607
+
1608
+ def write(self, pid: ta.Optional[int] = None) -> None:
1609
+ self.ensure_locked()
1610
+ if pid is None:
1611
+ pid = os.getpid()
1612
+ self._f.write(f'{pid}\n')
1613
+ self._f.flush()
1614
+
1615
+ def clear(self) -> None:
1616
+ self.ensure_locked()
1617
+ self._f.seek(0)
1618
+ self._f.truncate()
1619
+
1620
+ def read(self) -> int:
1621
+ if self.try_lock():
1622
+ raise RuntimeError('Got lock')
1623
+ self._f.seek(0)
1624
+ return int(self._f.read())
1625
+
1626
+ def kill(self, sig: int = signal.SIGTERM) -> None:
1627
+ pid = self.read()
1628
+ os.kill(pid, sig) # FIXME: Still racy
1629
+
1630
+
1631
1631
  ########################################
1632
1632
  # ../../auth.py
1633
1633
  """
@@ -103,7 +103,7 @@ InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
103
103
  InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
104
104
 
105
105
  # ../../omlish/lite/subprocesses.py
106
- SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
106
+ SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
107
107
 
108
108
 
109
109
  ########################################
@@ -1611,30 +1611,6 @@ class Checks:
1611
1611
  check = Checks()
1612
1612
 
1613
1613
 
1614
- ########################################
1615
- # ../../../omlish/lite/deathsig.py
1616
-
1617
-
1618
- LINUX_PR_SET_PDEATHSIG = 1 # Second arg is a signal
1619
- LINUX_PR_GET_PDEATHSIG = 2 # Second arg is a ptr to return the signal
1620
-
1621
-
1622
- def set_process_deathsig(sig: int) -> bool:
1623
- if sys.platform == 'linux':
1624
- libc = ct.CDLL('libc.so.6')
1625
-
1626
- # int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
1627
- libc.prctl.restype = ct.c_int
1628
- libc.prctl.argtypes = [ct.c_int, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong]
1629
-
1630
- libc.prctl(LINUX_PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
1631
-
1632
- return True
1633
-
1634
- else:
1635
- return False
1636
-
1637
-
1638
1614
  ########################################
1639
1615
  # ../../../omlish/lite/json.py
1640
1616
 
@@ -1880,6 +1856,30 @@ def format_num_bytes(num_bytes: int) -> str:
1880
1856
  return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
1881
1857
 
1882
1858
 
1859
+ ########################################
1860
+ # ../../../omlish/os/deathsig.py
1861
+
1862
+
1863
+ LINUX_PR_SET_PDEATHSIG = 1 # Second arg is a signal
1864
+ LINUX_PR_GET_PDEATHSIG = 2 # Second arg is a ptr to return the signal
1865
+
1866
+
1867
+ def set_process_deathsig(sig: int) -> bool:
1868
+ if sys.platform == 'linux':
1869
+ libc = ct.CDLL('libc.so.6')
1870
+
1871
+ # int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
1872
+ libc.prctl.restype = ct.c_int
1873
+ libc.prctl.argtypes = [ct.c_int, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong]
1874
+
1875
+ libc.prctl(LINUX_PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
1876
+
1877
+ return True
1878
+
1879
+ else:
1880
+ return False
1881
+
1882
+
1883
1883
  ########################################
1884
1884
  # ../../../omdev/packaging/specifiers.py
1885
1885
  # Copyright (c) Donald Stufft and individual contributors.
@@ -2618,6 +2618,8 @@ def get_remote_payload_src(
2618
2618
  TODO:
2619
2619
  - default command
2620
2620
  - auto match all underscores to hyphens
2621
+ - pre-run, post-run hooks
2622
+ - exitstack?
2621
2623
  """
2622
2624
 
2623
2625
 
@@ -2737,11 +2739,12 @@ class ArgparseCli:
2737
2739
 
2738
2740
  self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
2739
2741
 
2742
+ #
2743
+
2740
2744
  def __init_subclass__(cls, **kwargs: ta.Any) -> None:
2741
2745
  super().__init_subclass__(**kwargs)
2742
2746
 
2743
2747
  ns = cls.__dict__
2744
-
2745
2748
  objs = {}
2746
2749
  mro = cls.__mro__[::-1]
2747
2750
  for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
@@ -2754,24 +2757,33 @@ class ArgparseCli:
2754
2757
  elif k in objs:
2755
2758
  del [k]
2756
2759
 
2760
+ #
2761
+
2757
2762
  anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
2758
2763
  **{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
2759
2764
  **ns.get('__annotations__', {}),
2760
2765
  }), globalns=ns.get('__globals__', {}))
2761
2766
 
2767
+ #
2768
+
2762
2769
  if '_parser' in ns:
2763
2770
  parser = check.isinstance(ns['_parser'], argparse.ArgumentParser)
2764
2771
  else:
2765
2772
  parser = argparse.ArgumentParser()
2766
2773
  setattr(cls, '_parser', parser)
2767
2774
 
2775
+ #
2776
+
2768
2777
  subparsers = parser.add_subparsers()
2778
+
2769
2779
  for att, obj in objs.items():
2770
2780
  if isinstance(obj, ArgparseCommand):
2771
2781
  if obj.parent is not None:
2772
2782
  raise NotImplementedError
2783
+
2773
2784
  for cn in [obj.name, *(obj.aliases or [])]:
2774
- cparser = subparsers.add_parser(cn)
2785
+ subparser = subparsers.add_parser(cn)
2786
+
2775
2787
  for arg in (obj.args or []):
2776
2788
  if (
2777
2789
  len(arg.args) == 1 and
@@ -2779,29 +2791,34 @@ class ArgparseCli:
2779
2791
  not (n := check.isinstance(arg.args[0], str)).startswith('-') and
2780
2792
  'metavar' not in arg.kwargs
2781
2793
  ):
2782
- cparser.add_argument(
2794
+ subparser.add_argument(
2783
2795
  n.replace('-', '_'),
2784
2796
  **arg.kwargs,
2785
2797
  metavar=n,
2786
2798
  )
2787
2799
  else:
2788
- cparser.add_argument(*arg.args, **arg.kwargs)
2789
- cparser.set_defaults(_cmd=obj)
2800
+ subparser.add_argument(*arg.args, **arg.kwargs)
2801
+
2802
+ subparser.set_defaults(_cmd=obj)
2790
2803
 
2791
2804
  elif isinstance(obj, ArgparseArg):
2792
2805
  if att in anns:
2793
- akwargs = _get_argparse_arg_ann_kwargs(anns[att])
2794
- obj.kwargs = {**akwargs, **obj.kwargs}
2806
+ ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
2807
+ obj.kwargs = {**ann_kwargs, **obj.kwargs}
2808
+
2795
2809
  if not obj.dest:
2796
2810
  if 'dest' in obj.kwargs:
2797
2811
  obj.dest = obj.kwargs['dest']
2798
2812
  else:
2799
2813
  obj.dest = obj.kwargs['dest'] = att # type: ignore
2814
+
2800
2815
  parser.add_argument(*obj.args, **obj.kwargs)
2801
2816
 
2802
2817
  else:
2803
2818
  raise TypeError(obj)
2804
2819
 
2820
+ #
2821
+
2805
2822
  _parser: ta.ClassVar[argparse.ArgumentParser]
2806
2823
 
2807
2824
  @classmethod
@@ -2820,10 +2837,12 @@ class ArgparseCli:
2820
2837
  def unknown_args(self) -> ta.Sequence[str]:
2821
2838
  return self._unknown_args
2822
2839
 
2823
- def _run_cmd(self, cmd: ArgparseCommand) -> ta.Optional[int]:
2824
- return cmd.__get__(self, type(self))()
2840
+ #
2841
+
2842
+ def _bind_cli_cmd(self, cmd: ArgparseCommand) -> ta.Callable:
2843
+ return cmd.__get__(self, type(self))
2825
2844
 
2826
- def __call__(self) -> ta.Optional[int]:
2845
+ def prepare_cli_run(self) -> ta.Optional[ta.Callable]:
2827
2846
  cmd = getattr(self.args, '_cmd', None)
2828
2847
 
2829
2848
  if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
@@ -2835,12 +2854,34 @@ class ArgparseCli:
2835
2854
 
2836
2855
  if cmd is None:
2837
2856
  self.get_parser().print_help()
2857
+ return None
2858
+
2859
+ return self._bind_cli_cmd(cmd)
2860
+
2861
+ #
2862
+
2863
+ def cli_run(self) -> ta.Optional[int]:
2864
+ if (fn := self.prepare_cli_run()) is None:
2838
2865
  return 0
2839
2866
 
2840
- return self._run_cmd(cmd)
2867
+ return fn()
2841
2868
 
2842
- def call_and_exit(self) -> ta.NoReturn:
2843
- sys.exit(rc if isinstance(rc := self(), int) else 0)
2869
+ def cli_run_and_exit(self) -> ta.NoReturn:
2870
+ sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
2871
+
2872
+ def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
2873
+ if exit:
2874
+ return self.cli_run_and_exit()
2875
+ else:
2876
+ return self.cli_run()
2877
+
2878
+ #
2879
+
2880
+ async def async_cli_run(self) -> ta.Optional[int]:
2881
+ if (fn := self.prepare_cli_run()) is None:
2882
+ return 0
2883
+
2884
+ return await fn()
2844
2885
 
2845
2886
 
2846
2887
  ########################################
@@ -6975,7 +7016,7 @@ def main_bootstrap(bs: MainBootstrap) -> Injector:
6975
7016
 
6976
7017
  class MainCli(ArgparseCli):
6977
7018
  @argparse_command(
6978
- argparse_arg('--payload-file'),
7019
+ argparse_arg('--_payload-file'),
6979
7020
 
6980
7021
  argparse_arg('-s', '--shell'),
6981
7022
  argparse_arg('-q', '--shell-quote', action='store_true'),
@@ -6993,10 +7034,7 @@ class MainCli(ArgparseCli):
6993
7034
 
6994
7035
  argparse_arg('command', nargs='+'),
6995
7036
  )
6996
- def run(self) -> None:
6997
- asyncio.run(self._async_run())
6998
-
6999
- async def _async_run(self) -> None:
7037
+ async def run(self) -> None:
7000
7038
  bs = MainBootstrap(
7001
7039
  main_config=MainConfig(
7002
7040
  log_level='DEBUG' if self.args.debug else 'INFO',
@@ -7005,7 +7043,7 @@ class MainCli(ArgparseCli):
7005
7043
  ),
7006
7044
 
7007
7045
  remote_config=RemoteConfig(
7008
- payload_file=self.args.payload_file, # noqa
7046
+ payload_file=self.args._payload_file, # noqa
7009
7047
 
7010
7048
  pycharm_remote_debug=PycharmRemoteDebug(
7011
7049
  port=self.args.pycharm_debug_port,
@@ -7068,7 +7106,7 @@ class MainCli(ArgparseCli):
7068
7106
 
7069
7107
 
7070
7108
  def _main() -> None:
7071
- MainCli().call_and_exit()
7109
+ sys.exit(asyncio.run(MainCli().async_cli_run()))
7072
7110
 
7073
7111
 
7074
7112
  if __name__ == '__main__':