ominfra 0.0.0.dev147__py3-none-any.whl → 0.0.0.dev149__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.
- ominfra/clouds/aws/cli.py +1 -1
- ominfra/configs.py +30 -5
- ominfra/journald/messages.py +1 -1
- ominfra/manage/commands/base.py +5 -5
- ominfra/manage/commands/execution.py +2 -2
- ominfra/manage/commands/inject.py +1 -1
- ominfra/manage/commands/interp.py +2 -2
- ominfra/manage/commands/subprocess.py +22 -14
- ominfra/manage/deploy/command.py +1 -1
- ominfra/manage/deploy/paths.py +2 -2
- ominfra/manage/main.py +48 -32
- ominfra/manage/remote/_main.py +172 -0
- ominfra/manage/remote/channel.py +41 -16
- ominfra/manage/remote/config.py +10 -0
- ominfra/manage/remote/connection.py +106 -0
- ominfra/manage/remote/execution.py +244 -155
- ominfra/manage/remote/inject.py +7 -3
- ominfra/manage/remote/spawning.py +51 -33
- ominfra/pyremote.py +28 -3
- ominfra/scripts/journald2aws.py +195 -91
- ominfra/scripts/manage.py +1366 -486
- ominfra/scripts/supervisor.py +533 -479
- ominfra/supervisor/dispatchers.py +1 -1
- ominfra/supervisor/http.py +2 -2
- ominfra/supervisor/inject.py +4 -4
- ominfra/supervisor/io.py +1 -1
- ominfra/supervisor/spawningimpl.py +1 -1
- ominfra/supervisor/supervisor.py +1 -1
- ominfra/supervisor/types.py +1 -1
- ominfra/tailscale/cli.py +1 -1
- {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/RECORD +36 -34
- {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,23 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
3
|
+
import asyncio
|
2
4
|
import contextlib
|
3
5
|
import dataclasses as dc
|
4
6
|
import shlex
|
5
7
|
import subprocess
|
6
8
|
import typing as ta
|
7
9
|
|
10
|
+
from omlish.lite.asyncio.subprocesses import asyncio_subprocess_popen
|
8
11
|
from omlish.lite.check import check_not_none
|
9
12
|
from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
|
10
13
|
from omlish.lite.subprocesses import SubprocessChannelOption
|
11
14
|
from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
|
12
15
|
|
13
16
|
|
14
|
-
|
17
|
+
##
|
18
|
+
|
19
|
+
|
20
|
+
class RemoteSpawning(abc.ABC):
|
15
21
|
@dc.dataclass(frozen=True)
|
16
22
|
class Target:
|
17
23
|
shell: ta.Optional[str] = None
|
@@ -22,15 +28,35 @@ class RemoteSpawning:
|
|
22
28
|
|
23
29
|
stderr: ta.Optional[str] = None # SubprocessChannelOption
|
24
30
|
|
25
|
-
|
31
|
+
@dc.dataclass(frozen=True)
|
32
|
+
class Spawned:
|
33
|
+
stdin: asyncio.StreamWriter
|
34
|
+
stdout: asyncio.StreamReader
|
35
|
+
stderr: ta.Optional[asyncio.StreamReader]
|
36
|
+
|
37
|
+
@abc.abstractmethod
|
38
|
+
def spawn(
|
39
|
+
self,
|
40
|
+
tgt: Target,
|
41
|
+
src: str,
|
42
|
+
*,
|
43
|
+
timeout: ta.Optional[float] = None,
|
44
|
+
debug: bool = False,
|
45
|
+
) -> ta.AsyncContextManager[Spawned]:
|
46
|
+
raise NotImplementedError
|
47
|
+
|
48
|
+
|
49
|
+
##
|
26
50
|
|
27
|
-
|
51
|
+
|
52
|
+
class SubprocessRemoteSpawning(RemoteSpawning):
|
53
|
+
class _PreparedCmd(ta.NamedTuple): # noqa
|
28
54
|
cmd: ta.Sequence[str]
|
29
55
|
shell: bool
|
30
56
|
|
31
57
|
def _prepare_cmd(
|
32
58
|
self,
|
33
|
-
tgt: Target,
|
59
|
+
tgt: RemoteSpawning.Target,
|
34
60
|
src: str,
|
35
61
|
) -> _PreparedCmd:
|
36
62
|
if tgt.shell is not None:
|
@@ -38,44 +64,38 @@ class RemoteSpawning:
|
|
38
64
|
if tgt.shell_quote:
|
39
65
|
sh_src = shlex.quote(sh_src)
|
40
66
|
sh_cmd = f'{tgt.shell} {sh_src}'
|
41
|
-
return
|
42
|
-
cmd=[sh_cmd],
|
43
|
-
shell=True,
|
44
|
-
)
|
67
|
+
return SubprocessRemoteSpawning._PreparedCmd([sh_cmd], shell=True)
|
45
68
|
|
46
69
|
else:
|
47
|
-
return
|
48
|
-
cmd=[tgt.python, '-c', src],
|
49
|
-
shell=False,
|
50
|
-
)
|
70
|
+
return SubprocessRemoteSpawning._PreparedCmd([tgt.python, '-c', src], shell=False)
|
51
71
|
|
52
72
|
#
|
53
73
|
|
54
|
-
@
|
55
|
-
|
56
|
-
stdin: ta.IO
|
57
|
-
stdout: ta.IO
|
58
|
-
stderr: ta.Optional[ta.IO]
|
59
|
-
|
60
|
-
@contextlib.contextmanager
|
61
|
-
def spawn(
|
74
|
+
@contextlib.asynccontextmanager
|
75
|
+
async def spawn(
|
62
76
|
self,
|
63
|
-
tgt: Target,
|
77
|
+
tgt: RemoteSpawning.Target,
|
64
78
|
src: str,
|
65
79
|
*,
|
66
80
|
timeout: ta.Optional[float] = None,
|
67
|
-
|
81
|
+
debug: bool = False,
|
82
|
+
) -> ta.AsyncGenerator[RemoteSpawning.Spawned, None]:
|
68
83
|
pc = self._prepare_cmd(tgt, src)
|
69
84
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
85
|
+
cmd = pc.cmd
|
86
|
+
if not debug:
|
87
|
+
cmd = subprocess_maybe_shell_wrap_exec(*cmd)
|
88
|
+
|
89
|
+
async with asyncio_subprocess_popen(
|
90
|
+
*cmd,
|
91
|
+
shell=pc.shell,
|
92
|
+
stdin=subprocess.PIPE,
|
93
|
+
stdout=subprocess.PIPE,
|
94
|
+
stderr=(
|
95
|
+
SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, tgt.stderr)]
|
96
|
+
if tgt.stderr is not None else None
|
97
|
+
),
|
98
|
+
timeout=timeout,
|
79
99
|
) as proc:
|
80
100
|
stdin = check_not_none(proc.stdin)
|
81
101
|
stdout = check_not_none(proc.stdout)
|
@@ -92,5 +112,3 @@ class RemoteSpawning:
|
|
92
112
|
stdin.close()
|
93
113
|
except BrokenPipeError:
|
94
114
|
pass
|
95
|
-
|
96
|
-
proc.wait(timeout)
|
ominfra/pyremote.py
CHANGED
@@ -324,10 +324,8 @@ def pyremote_bootstrap_finalize() -> PyremotePayloadRuntime:
|
|
324
324
|
with open(os.environ.pop(_PYREMOTE_BOOTSTRAP_SRC_FILE_VAR)) as sf:
|
325
325
|
main_src = sf.read()
|
326
326
|
|
327
|
-
# Restore
|
327
|
+
# Restore vars
|
328
328
|
sys.executable = os.environ.pop(_PYREMOTE_BOOTSTRAP_ARGV0_VAR)
|
329
|
-
|
330
|
-
# Grab context name
|
331
329
|
context_name = os.environ.pop(_PYREMOTE_BOOTSTRAP_CONTEXT_NAME_VAR)
|
332
330
|
|
333
331
|
# Write third ack
|
@@ -501,3 +499,30 @@ class PyremoteBootstrapDriver:
|
|
501
499
|
output.flush()
|
502
500
|
else:
|
503
501
|
raise TypeError(go)
|
502
|
+
|
503
|
+
async def async_run(
|
504
|
+
self,
|
505
|
+
input: ta.Any, # asyncio.StreamWriter # noqa
|
506
|
+
output: ta.Any, # asyncio.StreamReader
|
507
|
+
) -> Result:
|
508
|
+
gen = self.gen()
|
509
|
+
|
510
|
+
gi: ta.Optional[bytes] = None
|
511
|
+
while True:
|
512
|
+
try:
|
513
|
+
if gi is not None:
|
514
|
+
go = gen.send(gi)
|
515
|
+
else:
|
516
|
+
go = next(gen)
|
517
|
+
except StopIteration as e:
|
518
|
+
return e.value
|
519
|
+
|
520
|
+
if isinstance(go, self.Read):
|
521
|
+
if len(gi := await input.read(go.sz)) != go.sz:
|
522
|
+
raise EOFError
|
523
|
+
elif isinstance(go, self.Write):
|
524
|
+
gi = None
|
525
|
+
output.write(go.d)
|
526
|
+
await output.drain()
|
527
|
+
else:
|
528
|
+
raise TypeError(go)
|
ominfra/scripts/journald2aws.py
CHANGED
@@ -898,7 +898,10 @@ def toml_make_safe_parse_float(parse_float: TomlParseFloat) -> TomlParseFloat:
|
|
898
898
|
# ../../../../../omlish/lite/cached.py
|
899
899
|
|
900
900
|
|
901
|
-
|
901
|
+
##
|
902
|
+
|
903
|
+
|
904
|
+
class _AbstractCachedNullary:
|
902
905
|
def __init__(self, fn):
|
903
906
|
super().__init__()
|
904
907
|
self._fn = fn
|
@@ -906,17 +909,25 @@ class _cached_nullary: # noqa
|
|
906
909
|
functools.update_wrapper(self, fn)
|
907
910
|
|
908
911
|
def __call__(self, *args, **kwargs): # noqa
|
909
|
-
|
910
|
-
self._value = self._fn()
|
911
|
-
return self._value
|
912
|
+
raise TypeError
|
912
913
|
|
913
914
|
def __get__(self, instance, owner): # noqa
|
914
915
|
bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
|
915
916
|
return bound
|
916
917
|
|
917
918
|
|
919
|
+
##
|
920
|
+
|
921
|
+
|
922
|
+
class _CachedNullary(_AbstractCachedNullary):
|
923
|
+
def __call__(self, *args, **kwargs): # noqa
|
924
|
+
if self._value is self._missing:
|
925
|
+
self._value = self._fn()
|
926
|
+
return self._value
|
927
|
+
|
928
|
+
|
918
929
|
def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
|
919
|
-
return
|
930
|
+
return _CachedNullary(fn)
|
920
931
|
|
921
932
|
|
922
933
|
def static_init(fn: CallableT) -> CallableT:
|
@@ -925,6 +936,20 @@ def static_init(fn: CallableT) -> CallableT:
|
|
925
936
|
return fn
|
926
937
|
|
927
938
|
|
939
|
+
##
|
940
|
+
|
941
|
+
|
942
|
+
class _AsyncCachedNullary(_AbstractCachedNullary):
|
943
|
+
async def __call__(self, *args, **kwargs):
|
944
|
+
if self._value is self._missing:
|
945
|
+
self._value = await self._fn()
|
946
|
+
return self._value
|
947
|
+
|
948
|
+
|
949
|
+
def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
|
950
|
+
return _AsyncCachedNullary(fn)
|
951
|
+
|
952
|
+
|
928
953
|
########################################
|
929
954
|
# ../../../../../omlish/lite/check.py
|
930
955
|
|
@@ -964,6 +989,11 @@ def check_non_empty_str(v: ta.Optional[str]) -> str:
|
|
964
989
|
return v
|
965
990
|
|
966
991
|
|
992
|
+
def check_arg(v: bool, msg: str = 'Illegal argument') -> None:
|
993
|
+
if not v:
|
994
|
+
raise ValueError(msg)
|
995
|
+
|
996
|
+
|
967
997
|
def check_state(v: bool, msg: str = 'Illegal state') -> None:
|
968
998
|
if not v:
|
969
999
|
raise ValueError(msg)
|
@@ -1016,7 +1046,7 @@ def check_empty(v: SizedT) -> SizedT:
|
|
1016
1046
|
return v
|
1017
1047
|
|
1018
1048
|
|
1019
|
-
def
|
1049
|
+
def check_not_empty(v: SizedT) -> SizedT:
|
1020
1050
|
if not len(v):
|
1021
1051
|
raise ValueError(v)
|
1022
1052
|
return v
|
@@ -1605,65 +1635,7 @@ class AwsDataclassMeta:
|
|
1605
1635
|
|
1606
1636
|
|
1607
1637
|
########################################
|
1608
|
-
# ../../../../../omlish/
|
1609
|
-
|
1610
|
-
|
1611
|
-
##
|
1612
|
-
|
1613
|
-
|
1614
|
-
class ExitStacked:
|
1615
|
-
_exit_stack: ta.Optional[contextlib.ExitStack] = None
|
1616
|
-
|
1617
|
-
def __enter__(self: ExitStackedT) -> ExitStackedT:
|
1618
|
-
check_state(self._exit_stack is None)
|
1619
|
-
es = self._exit_stack = contextlib.ExitStack()
|
1620
|
-
es.__enter__()
|
1621
|
-
return self
|
1622
|
-
|
1623
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
1624
|
-
if (es := self._exit_stack) is None:
|
1625
|
-
return None
|
1626
|
-
self._exit_contexts()
|
1627
|
-
return es.__exit__(exc_type, exc_val, exc_tb)
|
1628
|
-
|
1629
|
-
def _exit_contexts(self) -> None:
|
1630
|
-
pass
|
1631
|
-
|
1632
|
-
def _enter_context(self, cm: ta.ContextManager[T]) -> T:
|
1633
|
-
es = check_not_none(self._exit_stack)
|
1634
|
-
return es.enter_context(cm)
|
1635
|
-
|
1636
|
-
|
1637
|
-
##
|
1638
|
-
|
1639
|
-
|
1640
|
-
@contextlib.contextmanager
|
1641
|
-
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
1642
|
-
try:
|
1643
|
-
yield fn
|
1644
|
-
finally:
|
1645
|
-
fn()
|
1646
|
-
|
1647
|
-
|
1648
|
-
@contextlib.contextmanager
|
1649
|
-
def attr_setting(obj, attr, val, *, default=None): # noqa
|
1650
|
-
not_set = object()
|
1651
|
-
orig = getattr(obj, attr, not_set)
|
1652
|
-
try:
|
1653
|
-
setattr(obj, attr, val)
|
1654
|
-
if orig is not not_set:
|
1655
|
-
yield orig
|
1656
|
-
else:
|
1657
|
-
yield default
|
1658
|
-
finally:
|
1659
|
-
if orig is not_set:
|
1660
|
-
delattr(obj, attr)
|
1661
|
-
else:
|
1662
|
-
setattr(obj, attr, orig)
|
1663
|
-
|
1664
|
-
|
1665
|
-
########################################
|
1666
|
-
# ../../../../../omlish/lite/io.py
|
1638
|
+
# ../../../../../omlish/io/buffers.py
|
1667
1639
|
|
1668
1640
|
|
1669
1641
|
class DelimitingBuffer:
|
@@ -1850,7 +1822,7 @@ class IncrementalWriteBuffer:
|
|
1850
1822
|
) -> None:
|
1851
1823
|
super().__init__()
|
1852
1824
|
|
1853
|
-
|
1825
|
+
check_not_empty(data)
|
1854
1826
|
self._len = len(data)
|
1855
1827
|
self._write_size = write_size
|
1856
1828
|
|
@@ -1865,11 +1837,11 @@ class IncrementalWriteBuffer:
|
|
1865
1837
|
return self._len - self._pos
|
1866
1838
|
|
1867
1839
|
def write(self, fn: ta.Callable[[bytes], int]) -> int:
|
1868
|
-
lst =
|
1840
|
+
lst = check_not_empty(self._lst)
|
1869
1841
|
|
1870
1842
|
t = 0
|
1871
1843
|
for i, d in enumerate(lst): # noqa
|
1872
|
-
n = fn(
|
1844
|
+
n = fn(check_not_empty(d))
|
1873
1845
|
if not n:
|
1874
1846
|
break
|
1875
1847
|
t += n
|
@@ -1884,6 +1856,64 @@ class IncrementalWriteBuffer:
|
|
1884
1856
|
return t
|
1885
1857
|
|
1886
1858
|
|
1859
|
+
########################################
|
1860
|
+
# ../../../../../omlish/lite/contextmanagers.py
|
1861
|
+
|
1862
|
+
|
1863
|
+
##
|
1864
|
+
|
1865
|
+
|
1866
|
+
class ExitStacked:
|
1867
|
+
_exit_stack: ta.Optional[contextlib.ExitStack] = None
|
1868
|
+
|
1869
|
+
def __enter__(self: ExitStackedT) -> ExitStackedT:
|
1870
|
+
check_state(self._exit_stack is None)
|
1871
|
+
es = self._exit_stack = contextlib.ExitStack()
|
1872
|
+
es.__enter__()
|
1873
|
+
return self
|
1874
|
+
|
1875
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
1876
|
+
if (es := self._exit_stack) is None:
|
1877
|
+
return None
|
1878
|
+
self._exit_contexts()
|
1879
|
+
return es.__exit__(exc_type, exc_val, exc_tb)
|
1880
|
+
|
1881
|
+
def _exit_contexts(self) -> None:
|
1882
|
+
pass
|
1883
|
+
|
1884
|
+
def _enter_context(self, cm: ta.ContextManager[T]) -> T:
|
1885
|
+
es = check_not_none(self._exit_stack)
|
1886
|
+
return es.enter_context(cm)
|
1887
|
+
|
1888
|
+
|
1889
|
+
##
|
1890
|
+
|
1891
|
+
|
1892
|
+
@contextlib.contextmanager
|
1893
|
+
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
1894
|
+
try:
|
1895
|
+
yield fn
|
1896
|
+
finally:
|
1897
|
+
fn()
|
1898
|
+
|
1899
|
+
|
1900
|
+
@contextlib.contextmanager
|
1901
|
+
def attr_setting(obj, attr, val, *, default=None): # noqa
|
1902
|
+
not_set = object()
|
1903
|
+
orig = getattr(obj, attr, not_set)
|
1904
|
+
try:
|
1905
|
+
setattr(obj, attr, val)
|
1906
|
+
if orig is not not_set:
|
1907
|
+
yield orig
|
1908
|
+
else:
|
1909
|
+
yield default
|
1910
|
+
finally:
|
1911
|
+
if orig is not_set:
|
1912
|
+
delattr(obj, attr)
|
1913
|
+
else:
|
1914
|
+
setattr(obj, attr, orig)
|
1915
|
+
|
1916
|
+
|
1887
1917
|
########################################
|
1888
1918
|
# ../../../../../omlish/lite/logs.py
|
1889
1919
|
"""
|
@@ -2831,6 +2861,33 @@ class AwsLogMessageBuilder:
|
|
2831
2861
|
# ../../../../configs.py
|
2832
2862
|
|
2833
2863
|
|
2864
|
+
def parse_config_file(
|
2865
|
+
name: str,
|
2866
|
+
f: ta.TextIO,
|
2867
|
+
) -> ConfigMapping:
|
2868
|
+
if name.endswith('.toml'):
|
2869
|
+
return toml_loads(f.read())
|
2870
|
+
|
2871
|
+
elif any(name.endswith(e) for e in ('.yml', '.yaml')):
|
2872
|
+
yaml = __import__('yaml')
|
2873
|
+
return yaml.safe_load(f)
|
2874
|
+
|
2875
|
+
elif name.endswith('.ini'):
|
2876
|
+
import configparser
|
2877
|
+
cp = configparser.ConfigParser()
|
2878
|
+
cp.read_file(f)
|
2879
|
+
config_dct: ta.Dict[str, ta.Any] = {}
|
2880
|
+
for sec in cp.sections():
|
2881
|
+
cd = config_dct
|
2882
|
+
for k in sec.split('.'):
|
2883
|
+
cd = cd.setdefault(k, {})
|
2884
|
+
cd.update(cp.items(sec))
|
2885
|
+
return config_dct
|
2886
|
+
|
2887
|
+
else:
|
2888
|
+
return json.loads(f.read())
|
2889
|
+
|
2890
|
+
|
2834
2891
|
def read_config_file(
|
2835
2892
|
path: str,
|
2836
2893
|
cls: ta.Type[T],
|
@@ -2838,13 +2895,10 @@ def read_config_file(
|
|
2838
2895
|
prepare: ta.Optional[ta.Callable[[ConfigMapping], ConfigMapping]] = None,
|
2839
2896
|
) -> T:
|
2840
2897
|
with open(path) as cf:
|
2841
|
-
|
2842
|
-
config_dct = toml_loads(cf.read())
|
2843
|
-
else:
|
2844
|
-
config_dct = json.loads(cf.read())
|
2898
|
+
config_dct = parse_config_file(os.path.basename(path), cf)
|
2845
2899
|
|
2846
2900
|
if prepare is not None:
|
2847
|
-
config_dct = prepare(config_dct)
|
2901
|
+
config_dct = prepare(config_dct)
|
2848
2902
|
|
2849
2903
|
return unmarshal_obj(config_dct, cls)
|
2850
2904
|
|
@@ -3177,7 +3231,7 @@ def subprocess_maybe_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
|
|
3177
3231
|
return args
|
3178
3232
|
|
3179
3233
|
|
3180
|
-
def
|
3234
|
+
def prepare_subprocess_invocation(
|
3181
3235
|
*args: str,
|
3182
3236
|
env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
3183
3237
|
extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
@@ -3185,9 +3239,9 @@ def _prepare_subprocess_invocation(
|
|
3185
3239
|
shell: bool = False,
|
3186
3240
|
**kwargs: ta.Any,
|
3187
3241
|
) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
|
3188
|
-
log.debug(args)
|
3242
|
+
log.debug('prepare_subprocess_invocation: args=%r', args)
|
3189
3243
|
if extra_env:
|
3190
|
-
log.debug(extra_env)
|
3244
|
+
log.debug('prepare_subprocess_invocation: extra_env=%r', extra_env)
|
3191
3245
|
|
3192
3246
|
if extra_env:
|
3193
3247
|
env = {**(env if env is not None else os.environ), **extra_env}
|
@@ -3206,14 +3260,46 @@ def _prepare_subprocess_invocation(
|
|
3206
3260
|
)
|
3207
3261
|
|
3208
3262
|
|
3209
|
-
|
3210
|
-
args, kwargs = _prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
|
3211
|
-
return subprocess.check_call(args, **kwargs) # type: ignore
|
3263
|
+
##
|
3212
3264
|
|
3213
3265
|
|
3214
|
-
|
3215
|
-
|
3216
|
-
|
3266
|
+
@contextlib.contextmanager
|
3267
|
+
def subprocess_common_context(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
|
3268
|
+
start_time = time.time()
|
3269
|
+
try:
|
3270
|
+
log.debug('subprocess_common_context.try: args=%r', args)
|
3271
|
+
yield
|
3272
|
+
|
3273
|
+
except Exception as exc: # noqa
|
3274
|
+
log.debug('subprocess_common_context.except: exc=%r', exc)
|
3275
|
+
raise
|
3276
|
+
|
3277
|
+
finally:
|
3278
|
+
end_time = time.time()
|
3279
|
+
elapsed_s = end_time - start_time
|
3280
|
+
log.debug('subprocess_common_context.finally: elapsed_s=%f args=%r', elapsed_s, args)
|
3281
|
+
|
3282
|
+
|
3283
|
+
##
|
3284
|
+
|
3285
|
+
|
3286
|
+
def subprocess_check_call(
|
3287
|
+
*args: str,
|
3288
|
+
stdout: ta.Any = sys.stderr,
|
3289
|
+
**kwargs: ta.Any,
|
3290
|
+
) -> None:
|
3291
|
+
args, kwargs = prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
|
3292
|
+
with subprocess_common_context(*args, **kwargs):
|
3293
|
+
return subprocess.check_call(args, **kwargs) # type: ignore
|
3294
|
+
|
3295
|
+
|
3296
|
+
def subprocess_check_output(
|
3297
|
+
*args: str,
|
3298
|
+
**kwargs: ta.Any,
|
3299
|
+
) -> bytes:
|
3300
|
+
args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
|
3301
|
+
with subprocess_common_context(*args, **kwargs):
|
3302
|
+
return subprocess.check_output(args, **kwargs)
|
3217
3303
|
|
3218
3304
|
|
3219
3305
|
def subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
|
@@ -3229,16 +3315,31 @@ DEFAULT_SUBPROCESS_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
|
|
3229
3315
|
)
|
3230
3316
|
|
3231
3317
|
|
3232
|
-
def
|
3233
|
-
|
3318
|
+
def _subprocess_try_run(
|
3319
|
+
fn: ta.Callable[..., T],
|
3320
|
+
*args: ta.Any,
|
3234
3321
|
try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
|
3235
3322
|
**kwargs: ta.Any,
|
3236
|
-
) ->
|
3323
|
+
) -> ta.Union[T, Exception]:
|
3237
3324
|
try:
|
3238
|
-
|
3325
|
+
return fn(*args, **kwargs)
|
3239
3326
|
except try_exceptions as e: # noqa
|
3240
3327
|
if log.isEnabledFor(logging.DEBUG):
|
3241
3328
|
log.exception('command failed')
|
3329
|
+
return e
|
3330
|
+
|
3331
|
+
|
3332
|
+
def subprocess_try_call(
|
3333
|
+
*args: str,
|
3334
|
+
try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
|
3335
|
+
**kwargs: ta.Any,
|
3336
|
+
) -> bool:
|
3337
|
+
if isinstance(_subprocess_try_run(
|
3338
|
+
subprocess_check_call,
|
3339
|
+
*args,
|
3340
|
+
try_exceptions=try_exceptions,
|
3341
|
+
**kwargs,
|
3342
|
+
), Exception):
|
3242
3343
|
return False
|
3243
3344
|
else:
|
3244
3345
|
return True
|
@@ -3249,12 +3350,15 @@ def subprocess_try_output(
|
|
3249
3350
|
try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
|
3250
3351
|
**kwargs: ta.Any,
|
3251
3352
|
) -> ta.Optional[bytes]:
|
3252
|
-
|
3253
|
-
|
3254
|
-
|
3255
|
-
|
3256
|
-
|
3353
|
+
if isinstance(ret := _subprocess_try_run(
|
3354
|
+
subprocess_check_output,
|
3355
|
+
*args,
|
3356
|
+
try_exceptions=try_exceptions,
|
3357
|
+
**kwargs,
|
3358
|
+
), Exception):
|
3257
3359
|
return None
|
3360
|
+
else:
|
3361
|
+
return ret
|
3258
3362
|
|
3259
3363
|
|
3260
3364
|
def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
|