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.
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]: