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.
Files changed (36) hide show
  1. ominfra/clouds/aws/cli.py +1 -1
  2. ominfra/configs.py +30 -5
  3. ominfra/journald/messages.py +1 -1
  4. ominfra/manage/commands/base.py +5 -5
  5. ominfra/manage/commands/execution.py +2 -2
  6. ominfra/manage/commands/inject.py +1 -1
  7. ominfra/manage/commands/interp.py +2 -2
  8. ominfra/manage/commands/subprocess.py +22 -14
  9. ominfra/manage/deploy/command.py +1 -1
  10. ominfra/manage/deploy/paths.py +2 -2
  11. ominfra/manage/main.py +48 -32
  12. ominfra/manage/remote/_main.py +172 -0
  13. ominfra/manage/remote/channel.py +41 -16
  14. ominfra/manage/remote/config.py +10 -0
  15. ominfra/manage/remote/connection.py +106 -0
  16. ominfra/manage/remote/execution.py +244 -155
  17. ominfra/manage/remote/inject.py +7 -3
  18. ominfra/manage/remote/spawning.py +51 -33
  19. ominfra/pyremote.py +28 -3
  20. ominfra/scripts/journald2aws.py +195 -91
  21. ominfra/scripts/manage.py +1366 -486
  22. ominfra/scripts/supervisor.py +533 -479
  23. ominfra/supervisor/dispatchers.py +1 -1
  24. ominfra/supervisor/http.py +2 -2
  25. ominfra/supervisor/inject.py +4 -4
  26. ominfra/supervisor/io.py +1 -1
  27. ominfra/supervisor/spawningimpl.py +1 -1
  28. ominfra/supervisor/supervisor.py +1 -1
  29. ominfra/supervisor/types.py +1 -1
  30. ominfra/tailscale/cli.py +1 -1
  31. {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/METADATA +3 -3
  32. {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/RECORD +36 -34
  33. {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/LICENSE +0 -0
  34. {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/WHEEL +0 -0
  35. {ominfra-0.0.0.dev147.dist-info → ominfra-0.0.0.dev149.dist-info}/entry_points.txt +0 -0
  36. {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
- class RemoteSpawning:
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
- class _PreparedCmd(ta.NamedTuple):
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 RemoteSpawning._PreparedCmd(
42
- cmd=[sh_cmd],
43
- shell=True,
44
- )
67
+ return SubprocessRemoteSpawning._PreparedCmd([sh_cmd], shell=True)
45
68
 
46
69
  else:
47
- return RemoteSpawning._PreparedCmd(
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
- @dc.dataclass(frozen=True)
55
- class Spawned:
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
- ) -> ta.Generator[Spawned, None, None]:
81
+ debug: bool = False,
82
+ ) -> ta.AsyncGenerator[RemoteSpawning.Spawned, None]:
68
83
  pc = self._prepare_cmd(tgt, src)
69
84
 
70
- with subprocess.Popen(
71
- subprocess_maybe_shell_wrap_exec(*pc.cmd),
72
- shell=pc.shell,
73
- stdin=subprocess.PIPE,
74
- stdout=subprocess.PIPE,
75
- stderr=(
76
- SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, tgt.stderr)]
77
- if tgt.stderr is not None else None
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 original argv0
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)
@@ -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
- class _cached_nullary: # noqa
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
- if self._value is self._missing:
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 _cached_nullary(fn)
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 check_non_empty(v: SizedT) -> SizedT:
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/lite/contextmanagers.py
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
- check_non_empty(data)
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 = check_non_empty(self._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(check_non_empty(d))
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
- if path.endswith('.toml'):
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) # type: ignore
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 _prepare_subprocess_invocation(
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
- def subprocess_check_call(*args: str, stdout=sys.stderr, **kwargs: ta.Any) -> None:
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
- def subprocess_check_output(*args: str, **kwargs: ta.Any) -> bytes:
3215
- args, kwargs = _prepare_subprocess_invocation(*args, **kwargs)
3216
- return subprocess.check_output(args, **kwargs)
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 subprocess_try_call(
3233
- *args: str,
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
- ) -> bool:
3323
+ ) -> ta.Union[T, Exception]:
3237
3324
  try:
3238
- subprocess_check_call(*args, **kwargs)
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
- try:
3253
- return subprocess_check_output(*args, **kwargs)
3254
- except try_exceptions as e: # noqa
3255
- if log.isEnabledFor(logging.DEBUG):
3256
- log.exception('command failed')
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]: