ominfra 0.0.0.dev147__py3-none-any.whl → 0.0.0.dev149__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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]:
|