ominfra 0.0.0.dev155__py3-none-any.whl → 0.0.0.dev157__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/manage/commands/subprocess.py +3 -4
- ominfra/manage/deploy/git.py +3 -3
- ominfra/manage/deploy/venvs.py +4 -4
- ominfra/manage/main.py +33 -4
- ominfra/manage/remote/spawning.py +3 -8
- ominfra/manage/system/packages.py +13 -15
- ominfra/scripts/journald2aws.py +184 -129
- ominfra/scripts/manage.py +1276 -325
- ominfra/scripts/supervisor.py +44 -31
- ominfra/supervisor/configs.py +2 -0
- ominfra/supervisor/supervisor.py +2 -33
- ominfra/supervisor/utils/os.py +41 -0
- {ominfra-0.0.0.dev155.dist-info → ominfra-0.0.0.dev157.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev155.dist-info → ominfra-0.0.0.dev157.dist-info}/RECORD +18 -18
- {ominfra-0.0.0.dev155.dist-info → ominfra-0.0.0.dev157.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev155.dist-info → ominfra-0.0.0.dev157.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev155.dist-info → ominfra-0.0.0.dev157.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev155.dist-info → ominfra-0.0.0.dev157.dist-info}/top_level.txt +0 -0
@@ -6,8 +6,7 @@ import subprocess
|
|
6
6
|
import time
|
7
7
|
import typing as ta
|
8
8
|
|
9
|
-
from omlish.lite.asyncio.subprocesses import
|
10
|
-
from omlish.lite.asyncio.subprocesses import asyncio_subprocess_popen
|
9
|
+
from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
|
11
10
|
from omlish.lite.check import check
|
12
11
|
from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
|
13
12
|
from omlish.lite.subprocesses import SubprocessChannelOption
|
@@ -51,7 +50,7 @@ class SubprocessCommand(Command['SubprocessCommand.Output']):
|
|
51
50
|
class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
|
52
51
|
async def execute(self, cmd: SubprocessCommand) -> SubprocessCommand.Output:
|
53
52
|
proc: asyncio.subprocess.Process
|
54
|
-
async with
|
53
|
+
async with asyncio_subprocesses.popen(
|
55
54
|
*subprocess_maybe_shell_wrap_exec(*cmd.cmd),
|
56
55
|
|
57
56
|
shell=cmd.shell,
|
@@ -65,7 +64,7 @@ class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCom
|
|
65
64
|
timeout=cmd.timeout,
|
66
65
|
) as proc:
|
67
66
|
start_time = time.time()
|
68
|
-
stdout, stderr = await
|
67
|
+
stdout, stderr = await asyncio_subprocesses.communicate(
|
69
68
|
proc,
|
70
69
|
input=cmd.input,
|
71
70
|
timeout=cmd.timeout,
|
ominfra/manage/deploy/git.py
CHANGED
@@ -13,7 +13,7 @@ import functools
|
|
13
13
|
import os.path
|
14
14
|
import typing as ta
|
15
15
|
|
16
|
-
from omlish.lite.asyncio.subprocesses import
|
16
|
+
from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
|
17
17
|
from omlish.lite.cached import async_cached_nullary
|
18
18
|
from omlish.lite.check import check
|
19
19
|
|
@@ -92,7 +92,7 @@ class DeployGitManager(DeployPathOwner):
|
|
92
92
|
return f'https://{self._repo.host}/{self._repo.path}'
|
93
93
|
|
94
94
|
async def _call(self, *cmd: str) -> None:
|
95
|
-
await
|
95
|
+
await asyncio_subprocesses.check_call(
|
96
96
|
*cmd,
|
97
97
|
cwd=self._dir,
|
98
98
|
)
|
@@ -118,7 +118,7 @@ class DeployGitManager(DeployPathOwner):
|
|
118
118
|
# FIXME: temp dir swap
|
119
119
|
os.makedirs(dst_dir)
|
120
120
|
|
121
|
-
dst_call = functools.partial(
|
121
|
+
dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_dir)
|
122
122
|
await dst_call('git', 'init')
|
123
123
|
|
124
124
|
await dst_call('git', 'remote', 'add', 'local', self._dir)
|
ominfra/manage/deploy/venvs.py
CHANGED
@@ -7,7 +7,7 @@ TODO:
|
|
7
7
|
import os.path
|
8
8
|
import typing as ta
|
9
9
|
|
10
|
-
from omlish.lite.asyncio.subprocesses import
|
10
|
+
from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
|
11
11
|
|
12
12
|
from .paths import DeployPath
|
13
13
|
from .paths import DeployPathOwner
|
@@ -40,7 +40,7 @@ class DeployVenvManager(DeployPathOwner):
|
|
40
40
|
) -> None:
|
41
41
|
sys_exe = 'python3'
|
42
42
|
|
43
|
-
await
|
43
|
+
await asyncio_subprocesses.check_call(sys_exe, '-m', 'venv', venv_dir)
|
44
44
|
|
45
45
|
#
|
46
46
|
|
@@ -52,12 +52,12 @@ class DeployVenvManager(DeployPathOwner):
|
|
52
52
|
|
53
53
|
if os.path.isfile(reqs_txt):
|
54
54
|
if use_uv:
|
55
|
-
await
|
55
|
+
await asyncio_subprocesses.check_call(venv_exe, '-m', 'pip', 'install', 'uv')
|
56
56
|
pip_cmd = ['-m', 'uv', 'pip']
|
57
57
|
else:
|
58
58
|
pip_cmd = ['-m', 'pip']
|
59
59
|
|
60
|
-
await
|
60
|
+
await asyncio_subprocesses.check_call(venv_exe, *pip_cmd,'install', '-r', reqs_txt)
|
61
61
|
|
62
62
|
async def setup_app_venv(self, app_tag: DeployAppTag) -> None:
|
63
63
|
await self.setup_venv(
|
ominfra/manage/main.py
CHANGED
@@ -6,18 +6,23 @@ manage.py -s 'docker run -i python:3.12'
|
|
6
6
|
manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
|
7
7
|
"""
|
8
8
|
import asyncio
|
9
|
+
import dataclasses as dc
|
9
10
|
import json
|
11
|
+
import os.path
|
10
12
|
import sys
|
11
13
|
import typing as ta
|
12
14
|
|
13
15
|
from omlish.argparse.cli import ArgparseCli
|
14
16
|
from omlish.argparse.cli import argparse_arg
|
15
17
|
from omlish.argparse.cli import argparse_command
|
18
|
+
from omlish.lite.cached import cached_nullary
|
19
|
+
from omlish.lite.check import check
|
16
20
|
from omlish.lite.logs import log # noqa
|
17
21
|
from omlish.lite.marshal import ObjMarshalerManager
|
18
22
|
from omlish.lite.marshal import ObjMarshalOptions
|
19
23
|
from omlish.lite.pycharm import PycharmRemoteDebug
|
20
24
|
|
25
|
+
from ..configs import read_config_file
|
21
26
|
from .bootstrap import MainBootstrap
|
22
27
|
from .bootstrap_ import main_bootstrap
|
23
28
|
from .commands.base import Command
|
@@ -28,7 +33,28 @@ from .targets.connection import ManageTargetConnector
|
|
28
33
|
from .targets.targets import ManageTarget
|
29
34
|
|
30
35
|
|
36
|
+
@dc.dataclass(frozen=True)
|
37
|
+
class ManageConfig:
|
38
|
+
targets: ta.Optional[ta.Mapping[str, ManageTarget]] = None
|
39
|
+
|
40
|
+
|
31
41
|
class MainCli(ArgparseCli):
|
42
|
+
config_file: ta.Optional[str] = argparse_arg('--config-file', help='Config file path') # type: ignore
|
43
|
+
|
44
|
+
@cached_nullary
|
45
|
+
def config(self) -> ManageConfig:
|
46
|
+
if (cf := self.config_file) is None:
|
47
|
+
cf = os.path.expanduser('~/.omlish/manage.yml')
|
48
|
+
if not os.path.isfile(cf):
|
49
|
+
cf = None
|
50
|
+
|
51
|
+
if cf is None:
|
52
|
+
return ManageConfig()
|
53
|
+
else:
|
54
|
+
return read_config_file(cf, ManageConfig)
|
55
|
+
|
56
|
+
#
|
57
|
+
|
32
58
|
@argparse_command(
|
33
59
|
argparse_arg('--_payload-file'),
|
34
60
|
|
@@ -80,10 +106,13 @@ class MainCli(ArgparseCli):
|
|
80
106
|
|
81
107
|
msh = injector[ObjMarshalerManager]
|
82
108
|
|
83
|
-
|
84
|
-
if not ts.startswith('{'):
|
85
|
-
|
86
|
-
|
109
|
+
tgt: ManageTarget
|
110
|
+
if not (ts := self.args.target).startswith('{'):
|
111
|
+
tgt = check.not_none(self.config().targets)[ts]
|
112
|
+
else:
|
113
|
+
tgt = msh.unmarshal_obj(json.loads(ts), ManageTarget)
|
114
|
+
|
115
|
+
#
|
87
116
|
|
88
117
|
cmds: ta.List[Command] = []
|
89
118
|
cmd: Command
|
@@ -7,11 +7,10 @@ import shlex
|
|
7
7
|
import subprocess
|
8
8
|
import typing as ta
|
9
9
|
|
10
|
-
from omlish.lite.asyncio.subprocesses import
|
10
|
+
from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
|
11
11
|
from omlish.lite.check import check
|
12
12
|
from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
|
13
13
|
from omlish.lite.subprocesses import SubprocessChannelOption
|
14
|
-
from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
|
15
14
|
|
16
15
|
|
17
16
|
##
|
@@ -82,12 +81,8 @@ class SubprocessRemoteSpawning(RemoteSpawning):
|
|
82
81
|
) -> ta.AsyncGenerator[RemoteSpawning.Spawned, None]:
|
83
82
|
pc = self._prepare_cmd(tgt, src)
|
84
83
|
|
85
|
-
|
86
|
-
|
87
|
-
cmd = subprocess_maybe_shell_wrap_exec(*cmd)
|
88
|
-
|
89
|
-
async with asyncio_subprocess_popen(
|
90
|
-
*cmd,
|
84
|
+
async with asyncio_subprocesses.popen(
|
85
|
+
*pc.cmd,
|
91
86
|
shell=pc.shell,
|
92
87
|
stdin=subprocess.PIPE,
|
93
88
|
stdout=subprocess.PIPE,
|
@@ -9,9 +9,7 @@ import json
|
|
9
9
|
import os
|
10
10
|
import typing as ta
|
11
11
|
|
12
|
-
from omlish.lite.asyncio.subprocesses import
|
13
|
-
from omlish.lite.asyncio.subprocesses import asyncio_subprocess_check_output
|
14
|
-
from omlish.lite.asyncio.subprocesses import asyncio_subprocess_run
|
12
|
+
from omlish.lite.asyncio.subprocesses import asyncio_subprocesses
|
15
13
|
from omlish.lite.check import check
|
16
14
|
|
17
15
|
|
@@ -44,10 +42,10 @@ class SystemPackageManager(abc.ABC):
|
|
44
42
|
|
45
43
|
class BrewSystemPackageManager(SystemPackageManager):
|
46
44
|
async def update(self) -> None:
|
47
|
-
await
|
45
|
+
await asyncio_subprocesses.check_call('brew', 'update')
|
48
46
|
|
49
47
|
async def upgrade(self) -> None:
|
50
|
-
await
|
48
|
+
await asyncio_subprocesses.check_call('brew', 'upgrade')
|
51
49
|
|
52
50
|
async def install(self, *packages: SystemPackageOrStr) -> None:
|
53
51
|
es: ta.List[str] = []
|
@@ -56,11 +54,11 @@ class BrewSystemPackageManager(SystemPackageManager):
|
|
56
54
|
es.append(p.name + (f'@{p.version}' if p.version is not None else ''))
|
57
55
|
else:
|
58
56
|
es.append(p)
|
59
|
-
await
|
57
|
+
await asyncio_subprocesses.check_call('brew', 'install', *es)
|
60
58
|
|
61
59
|
async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
|
62
60
|
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
|
63
|
-
o = await
|
61
|
+
o = await asyncio_subprocesses.check_output('brew', 'info', '--json', *pns)
|
64
62
|
j = json.loads(o.decode())
|
65
63
|
d: ta.Dict[str, SystemPackage] = {}
|
66
64
|
for e in j:
|
@@ -79,18 +77,18 @@ class AptSystemPackageManager(SystemPackageManager):
|
|
79
77
|
}
|
80
78
|
|
81
79
|
async def update(self) -> None:
|
82
|
-
await
|
80
|
+
await asyncio_subprocesses.check_call('sudo', 'apt', 'update', env={**os.environ, **self._APT_ENV})
|
83
81
|
|
84
82
|
async def upgrade(self) -> None:
|
85
|
-
await
|
83
|
+
await asyncio_subprocesses.check_call('sudo', 'apt', 'upgrade', '-y', env={**os.environ, **self._APT_ENV})
|
86
84
|
|
87
85
|
async def install(self, *packages: SystemPackageOrStr) -> None:
|
88
86
|
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
|
89
|
-
await
|
87
|
+
await asyncio_subprocesses.check_call('sudo', 'apt', 'install', '-y', *pns, env={**os.environ, **self._APT_ENV})
|
90
88
|
|
91
89
|
async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
|
92
90
|
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
|
93
|
-
out = await
|
91
|
+
out = await asyncio_subprocesses.run(
|
94
92
|
'dpkg-query', '-W', '-f=${Package}=${Version}\n', *pns,
|
95
93
|
capture_output=True,
|
96
94
|
check=False,
|
@@ -107,20 +105,20 @@ class AptSystemPackageManager(SystemPackageManager):
|
|
107
105
|
|
108
106
|
class YumSystemPackageManager(SystemPackageManager):
|
109
107
|
async def update(self) -> None:
|
110
|
-
await
|
108
|
+
await asyncio_subprocesses.check_call('sudo', 'yum', 'check-update')
|
111
109
|
|
112
110
|
async def upgrade(self) -> None:
|
113
|
-
await
|
111
|
+
await asyncio_subprocesses.check_call('sudo', 'yum', 'update')
|
114
112
|
|
115
113
|
async def install(self, *packages: SystemPackageOrStr) -> None:
|
116
114
|
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
|
117
|
-
await
|
115
|
+
await asyncio_subprocesses.check_call('sudo', 'yum', 'install', *pns)
|
118
116
|
|
119
117
|
async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
|
120
118
|
pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
|
121
119
|
d: ta.Dict[str, SystemPackage] = {}
|
122
120
|
for pn in pns:
|
123
|
-
out = await
|
121
|
+
out = await asyncio_subprocesses.run(
|
124
122
|
'rpm', '-q', pn,
|
125
123
|
capture_output=True,
|
126
124
|
)
|
ominfra/scripts/journald2aws.py
CHANGED
@@ -932,8 +932,8 @@ class _CachedNullary(_AbstractCachedNullary):
|
|
932
932
|
return self._value
|
933
933
|
|
934
934
|
|
935
|
-
def cached_nullary(fn
|
936
|
-
return _CachedNullary(fn)
|
935
|
+
def cached_nullary(fn: CallableT) -> CallableT:
|
936
|
+
return _CachedNullary(fn) # type: ignore
|
937
937
|
|
938
938
|
|
939
939
|
def static_init(fn: CallableT) -> CallableT:
|
@@ -2559,6 +2559,7 @@ TODO:
|
|
2559
2559
|
- pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
|
2560
2560
|
- namedtuple
|
2561
2561
|
- literals
|
2562
|
+
- newtypes?
|
2562
2563
|
"""
|
2563
2564
|
|
2564
2565
|
|
@@ -3593,168 +3594,222 @@ SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
|
|
3593
3594
|
_SUBPROCESS_SHELL_WRAP_EXECS = False
|
3594
3595
|
|
3595
3596
|
|
3596
|
-
def subprocess_shell_wrap_exec(*
|
3597
|
-
return ('sh', '-c', ' '.join(map(shlex.quote,
|
3597
|
+
def subprocess_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
|
3598
|
+
return ('sh', '-c', ' '.join(map(shlex.quote, cmd)))
|
3598
3599
|
|
3599
3600
|
|
3600
|
-
def subprocess_maybe_shell_wrap_exec(*
|
3601
|
+
def subprocess_maybe_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
|
3601
3602
|
if _SUBPROCESS_SHELL_WRAP_EXECS or is_debugger_attached():
|
3602
|
-
return subprocess_shell_wrap_exec(*
|
3603
|
+
return subprocess_shell_wrap_exec(*cmd)
|
3603
3604
|
else:
|
3604
|
-
return
|
3605
|
-
|
3606
|
-
|
3607
|
-
def prepare_subprocess_invocation(
|
3608
|
-
*args: str,
|
3609
|
-
env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
3610
|
-
extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
3611
|
-
quiet: bool = False,
|
3612
|
-
shell: bool = False,
|
3613
|
-
**kwargs: ta.Any,
|
3614
|
-
) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
|
3615
|
-
log.debug('prepare_subprocess_invocation: args=%r', args)
|
3616
|
-
if extra_env:
|
3617
|
-
log.debug('prepare_subprocess_invocation: extra_env=%r', extra_env)
|
3618
|
-
|
3619
|
-
if extra_env:
|
3620
|
-
env = {**(env if env is not None else os.environ), **extra_env}
|
3621
|
-
|
3622
|
-
if quiet and 'stderr' not in kwargs:
|
3623
|
-
if not log.isEnabledFor(logging.DEBUG):
|
3624
|
-
kwargs['stderr'] = subprocess.DEVNULL
|
3625
|
-
|
3626
|
-
if not shell:
|
3627
|
-
args = subprocess_maybe_shell_wrap_exec(*args)
|
3628
|
-
|
3629
|
-
return args, dict(
|
3630
|
-
env=env,
|
3631
|
-
shell=shell,
|
3632
|
-
**kwargs,
|
3633
|
-
)
|
3605
|
+
return cmd
|
3634
3606
|
|
3635
3607
|
|
3636
3608
|
##
|
3637
3609
|
|
3638
3610
|
|
3639
|
-
|
3640
|
-
|
3641
|
-
|
3642
|
-
|
3643
|
-
|
3644
|
-
|
3645
|
-
|
3646
|
-
|
3647
|
-
|
3648
|
-
|
3611
|
+
def subprocess_close(
|
3612
|
+
proc: subprocess.Popen,
|
3613
|
+
timeout: ta.Optional[float] = None,
|
3614
|
+
) -> None:
|
3615
|
+
# TODO: terminate, sleep, kill
|
3616
|
+
if proc.stdout:
|
3617
|
+
proc.stdout.close()
|
3618
|
+
if proc.stderr:
|
3619
|
+
proc.stderr.close()
|
3620
|
+
if proc.stdin:
|
3621
|
+
proc.stdin.close()
|
3649
3622
|
|
3650
|
-
|
3651
|
-
end_time = time.time()
|
3652
|
-
elapsed_s = end_time - start_time
|
3653
|
-
log.debug('subprocess_common_context.finally: elapsed_s=%f args=%r', elapsed_s, args)
|
3623
|
+
proc.wait(timeout)
|
3654
3624
|
|
3655
3625
|
|
3656
3626
|
##
|
3657
3627
|
|
3658
3628
|
|
3659
|
-
|
3660
|
-
|
3661
|
-
|
3662
|
-
|
3663
|
-
|
3664
|
-
|
3665
|
-
|
3666
|
-
|
3629
|
+
class AbstractSubprocesses(abc.ABC): # noqa
|
3630
|
+
DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = log
|
3631
|
+
|
3632
|
+
def __init__(
|
3633
|
+
self,
|
3634
|
+
*,
|
3635
|
+
log: ta.Optional[logging.Logger] = None,
|
3636
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
3637
|
+
) -> None:
|
3638
|
+
super().__init__()
|
3667
3639
|
|
3640
|
+
self._log = log if log is not None else self.DEFAULT_LOGGER
|
3641
|
+
self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
|
3668
3642
|
|
3669
|
-
|
3670
|
-
*args: str,
|
3671
|
-
**kwargs: ta.Any,
|
3672
|
-
) -> bytes:
|
3673
|
-
args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
|
3674
|
-
with subprocess_common_context(*args, **kwargs):
|
3675
|
-
return subprocess.check_output(args, **kwargs)
|
3643
|
+
#
|
3676
3644
|
|
3645
|
+
def prepare_args(
|
3646
|
+
self,
|
3647
|
+
*cmd: str,
|
3648
|
+
env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
3649
|
+
extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
3650
|
+
quiet: bool = False,
|
3651
|
+
shell: bool = False,
|
3652
|
+
**kwargs: ta.Any,
|
3653
|
+
) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
|
3654
|
+
if self._log:
|
3655
|
+
self._log.debug('Subprocesses.prepare_args: cmd=%r', cmd)
|
3656
|
+
if extra_env:
|
3657
|
+
self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
|
3677
3658
|
|
3678
|
-
|
3679
|
-
|
3659
|
+
if extra_env:
|
3660
|
+
env = {**(env if env is not None else os.environ), **extra_env}
|
3680
3661
|
|
3662
|
+
if quiet and 'stderr' not in kwargs:
|
3663
|
+
if self._log and not self._log.isEnabledFor(logging.DEBUG):
|
3664
|
+
kwargs['stderr'] = subprocess.DEVNULL
|
3681
3665
|
|
3682
|
-
|
3666
|
+
if not shell:
|
3667
|
+
cmd = subprocess_maybe_shell_wrap_exec(*cmd)
|
3683
3668
|
|
3669
|
+
return cmd, dict(
|
3670
|
+
env=env,
|
3671
|
+
shell=shell,
|
3672
|
+
**kwargs,
|
3673
|
+
)
|
3684
3674
|
|
3685
|
-
|
3686
|
-
|
3687
|
-
|
3688
|
-
|
3675
|
+
@contextlib.contextmanager
|
3676
|
+
def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
|
3677
|
+
start_time = time.time()
|
3678
|
+
try:
|
3679
|
+
if self._log:
|
3680
|
+
self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
|
3681
|
+
yield
|
3689
3682
|
|
3683
|
+
except Exception as exc: # noqa
|
3684
|
+
if self._log:
|
3685
|
+
self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
|
3686
|
+
raise
|
3690
3687
|
|
3691
|
-
|
3692
|
-
|
3693
|
-
|
3694
|
-
|
3695
|
-
|
3696
|
-
) -> ta.Union[T, Exception]:
|
3697
|
-
try:
|
3698
|
-
return fn(*args, **kwargs)
|
3699
|
-
except try_exceptions as e: # noqa
|
3700
|
-
if log.isEnabledFor(logging.DEBUG):
|
3701
|
-
log.exception('command failed')
|
3702
|
-
return e
|
3703
|
-
|
3704
|
-
|
3705
|
-
def subprocess_try_call(
|
3706
|
-
*args: str,
|
3707
|
-
try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
|
3708
|
-
**kwargs: ta.Any,
|
3709
|
-
) -> bool:
|
3710
|
-
if isinstance(_subprocess_try_run(
|
3711
|
-
subprocess_check_call,
|
3712
|
-
*args,
|
3713
|
-
try_exceptions=try_exceptions,
|
3714
|
-
**kwargs,
|
3715
|
-
), Exception):
|
3716
|
-
return False
|
3717
|
-
else:
|
3718
|
-
return True
|
3688
|
+
finally:
|
3689
|
+
end_time = time.time()
|
3690
|
+
elapsed_s = end_time - start_time
|
3691
|
+
if self._log:
|
3692
|
+
self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
|
3719
3693
|
|
3694
|
+
@contextlib.contextmanager
|
3695
|
+
def prepare_and_wrap(
|
3696
|
+
self,
|
3697
|
+
*cmd: ta.Any,
|
3698
|
+
**kwargs: ta.Any,
|
3699
|
+
) -> ta.Iterator[ta.Tuple[
|
3700
|
+
ta.Tuple[ta.Any, ...],
|
3701
|
+
ta.Dict[str, ta.Any],
|
3702
|
+
]]:
|
3703
|
+
cmd, kwargs = self.prepare_args(*cmd, **kwargs)
|
3704
|
+
with self.wrap_call(*cmd, **kwargs):
|
3705
|
+
yield cmd, kwargs
|
3720
3706
|
|
3721
|
-
|
3722
|
-
|
3723
|
-
|
3724
|
-
|
3725
|
-
|
3726
|
-
|
3727
|
-
subprocess_check_output,
|
3728
|
-
*args,
|
3729
|
-
try_exceptions=try_exceptions,
|
3730
|
-
**kwargs,
|
3731
|
-
), Exception):
|
3732
|
-
return None
|
3733
|
-
else:
|
3734
|
-
return ret
|
3707
|
+
#
|
3708
|
+
|
3709
|
+
DEFAULT_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
|
3710
|
+
FileNotFoundError,
|
3711
|
+
subprocess.CalledProcessError,
|
3712
|
+
)
|
3735
3713
|
|
3714
|
+
def try_fn(
|
3715
|
+
self,
|
3716
|
+
fn: ta.Callable[..., T],
|
3717
|
+
*cmd: str,
|
3718
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
3719
|
+
**kwargs: ta.Any,
|
3720
|
+
) -> ta.Union[T, Exception]:
|
3721
|
+
if try_exceptions is None:
|
3722
|
+
try_exceptions = self._try_exceptions
|
3736
3723
|
|
3737
|
-
|
3738
|
-
|
3739
|
-
|
3724
|
+
try:
|
3725
|
+
return fn(*cmd, **kwargs)
|
3726
|
+
|
3727
|
+
except try_exceptions as e: # noqa
|
3728
|
+
if self._log and self._log.isEnabledFor(logging.DEBUG):
|
3729
|
+
self._log.exception('command failed')
|
3730
|
+
return e
|
3731
|
+
|
3732
|
+
async def async_try_fn(
|
3733
|
+
self,
|
3734
|
+
fn: ta.Callable[..., ta.Awaitable[T]],
|
3735
|
+
*cmd: ta.Any,
|
3736
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
3737
|
+
**kwargs: ta.Any,
|
3738
|
+
) -> ta.Union[T, Exception]:
|
3739
|
+
if try_exceptions is None:
|
3740
|
+
try_exceptions = self._try_exceptions
|
3741
|
+
|
3742
|
+
try:
|
3743
|
+
return await fn(*cmd, **kwargs)
|
3744
|
+
|
3745
|
+
except try_exceptions as e: # noqa
|
3746
|
+
if self._log and self._log.isEnabledFor(logging.DEBUG):
|
3747
|
+
self._log.exception('command failed')
|
3748
|
+
return e
|
3740
3749
|
|
3741
3750
|
|
3742
3751
|
##
|
3743
3752
|
|
3744
3753
|
|
3745
|
-
|
3746
|
-
|
3747
|
-
|
3748
|
-
|
3749
|
-
|
3750
|
-
|
3751
|
-
|
3752
|
-
|
3753
|
-
|
3754
|
-
if proc.stdin:
|
3755
|
-
proc.stdin.close()
|
3754
|
+
class Subprocesses(AbstractSubprocesses):
|
3755
|
+
def check_call(
|
3756
|
+
self,
|
3757
|
+
*cmd: str,
|
3758
|
+
stdout: ta.Any = sys.stderr,
|
3759
|
+
**kwargs: ta.Any,
|
3760
|
+
) -> None:
|
3761
|
+
with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
|
3762
|
+
subprocess.check_call(cmd, **kwargs)
|
3756
3763
|
|
3757
|
-
|
3764
|
+
def check_output(
|
3765
|
+
self,
|
3766
|
+
*cmd: str,
|
3767
|
+
**kwargs: ta.Any,
|
3768
|
+
) -> bytes:
|
3769
|
+
with self.prepare_and_wrap(*cmd, **kwargs) as (cmd, kwargs): # noqa
|
3770
|
+
return subprocess.check_output(cmd, **kwargs)
|
3771
|
+
|
3772
|
+
def check_output_str(
|
3773
|
+
self,
|
3774
|
+
*cmd: str,
|
3775
|
+
**kwargs: ta.Any,
|
3776
|
+
) -> str:
|
3777
|
+
return self.check_output(*cmd, **kwargs).decode().strip()
|
3778
|
+
|
3779
|
+
#
|
3780
|
+
|
3781
|
+
def try_call(
|
3782
|
+
self,
|
3783
|
+
*cmd: str,
|
3784
|
+
**kwargs: ta.Any,
|
3785
|
+
) -> bool:
|
3786
|
+
if isinstance(self.try_fn(self.check_call, *cmd, **kwargs), Exception):
|
3787
|
+
return False
|
3788
|
+
else:
|
3789
|
+
return True
|
3790
|
+
|
3791
|
+
def try_output(
|
3792
|
+
self,
|
3793
|
+
*cmd: str,
|
3794
|
+
**kwargs: ta.Any,
|
3795
|
+
) -> ta.Optional[bytes]:
|
3796
|
+
if isinstance(ret := self.try_fn(self.check_output, *cmd, **kwargs), Exception):
|
3797
|
+
return None
|
3798
|
+
else:
|
3799
|
+
return ret
|
3800
|
+
|
3801
|
+
def try_output_str(
|
3802
|
+
self,
|
3803
|
+
*cmd: str,
|
3804
|
+
**kwargs: ta.Any,
|
3805
|
+
) -> ta.Optional[str]:
|
3806
|
+
if (ret := self.try_output(*cmd, **kwargs)) is None:
|
3807
|
+
return None
|
3808
|
+
else:
|
3809
|
+
return ret.decode().strip()
|
3810
|
+
|
3811
|
+
|
3812
|
+
subprocesses = Subprocesses()
|
3758
3813
|
|
3759
3814
|
|
3760
3815
|
########################################
|