omlish 0.0.0.dev155__py3-none-any.whl → 0.0.0.dev157__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.
- omlish/__about__.py +2 -2
- omlish/argparse/cli.py +4 -0
- omlish/lite/asyncio/subprocesses.py +131 -172
- omlish/lite/cached.py +2 -2
- omlish/lite/marshal.py +1 -0
- omlish/lite/subprocesses.py +196 -141
- {omlish-0.0.0.dev155.dist-info → omlish-0.0.0.dev157.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev155.dist-info → omlish-0.0.0.dev157.dist-info}/RECORD +12 -12
- {omlish-0.0.0.dev155.dist-info → omlish-0.0.0.dev157.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev155.dist-info → omlish-0.0.0.dev157.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev155.dist-info → omlish-0.0.0.dev157.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev155.dist-info → omlish-0.0.0.dev157.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/argparse/cli.py
CHANGED
@@ -14,6 +14,8 @@ import sys
|
|
14
14
|
import typing as ta
|
15
15
|
|
16
16
|
from ..lite.check import check
|
17
|
+
from ..lite.reflect import get_optional_alias_arg
|
18
|
+
from ..lite.reflect import is_optional_alias
|
17
19
|
|
18
20
|
|
19
21
|
T = ta.TypeVar('T')
|
@@ -120,6 +122,8 @@ def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
|
|
120
122
|
return {'action': 'store_true'}
|
121
123
|
elif ann is list:
|
122
124
|
return {'action': 'append'}
|
125
|
+
elif is_optional_alias(ann):
|
126
|
+
return _get_argparse_arg_ann_kwargs(get_optional_alias_arg(ann))
|
123
127
|
else:
|
124
128
|
raise TypeError(ann)
|
125
129
|
|
@@ -4,7 +4,6 @@ import asyncio.subprocess
|
|
4
4
|
import contextlib
|
5
5
|
import dataclasses as dc
|
6
6
|
import functools
|
7
|
-
import logging
|
8
7
|
import subprocess
|
9
8
|
import sys
|
10
9
|
import typing as ta
|
@@ -12,9 +11,7 @@ import typing as ta
|
|
12
11
|
from ...asyncs.asyncio.timeouts import asyncio_maybe_timeout
|
13
12
|
from ..check import check
|
14
13
|
from ..logs import log
|
15
|
-
from ..subprocesses import
|
16
|
-
from ..subprocesses import prepare_subprocess_invocation
|
17
|
-
from ..subprocesses import subprocess_common_context
|
14
|
+
from ..subprocesses import AbstractSubprocesses
|
18
15
|
|
19
16
|
|
20
17
|
T = ta.TypeVar('T')
|
@@ -23,43 +20,6 @@ T = ta.TypeVar('T')
|
|
23
20
|
##
|
24
21
|
|
25
22
|
|
26
|
-
@contextlib.asynccontextmanager
|
27
|
-
async def asyncio_subprocess_popen(
|
28
|
-
*cmd: str,
|
29
|
-
shell: bool = False,
|
30
|
-
timeout: ta.Optional[float] = None,
|
31
|
-
**kwargs: ta.Any,
|
32
|
-
) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
|
33
|
-
fac: ta.Any
|
34
|
-
if shell:
|
35
|
-
fac = functools.partial(
|
36
|
-
asyncio.create_subprocess_shell,
|
37
|
-
check.single(cmd),
|
38
|
-
)
|
39
|
-
else:
|
40
|
-
fac = functools.partial(
|
41
|
-
asyncio.create_subprocess_exec,
|
42
|
-
*cmd,
|
43
|
-
)
|
44
|
-
|
45
|
-
with subprocess_common_context(
|
46
|
-
*cmd,
|
47
|
-
shell=shell,
|
48
|
-
timeout=timeout,
|
49
|
-
**kwargs,
|
50
|
-
):
|
51
|
-
proc: asyncio.subprocess.Process
|
52
|
-
proc = await fac(**kwargs)
|
53
|
-
try:
|
54
|
-
yield proc
|
55
|
-
|
56
|
-
finally:
|
57
|
-
await asyncio_maybe_timeout(proc.wait(), timeout)
|
58
|
-
|
59
|
-
|
60
|
-
##
|
61
|
-
|
62
|
-
|
63
23
|
class AsyncioProcessCommunicator:
|
64
24
|
def __init__(
|
65
25
|
self,
|
@@ -170,145 +130,144 @@ class AsyncioProcessCommunicator:
|
|
170
130
|
return await asyncio_maybe_timeout(self._communicate(input), timeout)
|
171
131
|
|
172
132
|
|
173
|
-
|
174
|
-
proc: asyncio.subprocess.Process,
|
175
|
-
input: ta.Any = None, # noqa
|
176
|
-
timeout: ta.Optional[float] = None,
|
177
|
-
) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
|
178
|
-
return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
|
179
|
-
|
180
|
-
|
181
|
-
@dc.dataclass(frozen=True)
|
182
|
-
class AsyncioSubprocessOutput:
|
183
|
-
proc: asyncio.subprocess.Process
|
184
|
-
stdout: ta.Optional[bytes]
|
185
|
-
stderr: ta.Optional[bytes]
|
186
|
-
|
187
|
-
|
188
|
-
async def asyncio_subprocess_run(
|
189
|
-
*args: str,
|
190
|
-
input: ta.Any = None, # noqa
|
191
|
-
timeout: ta.Optional[float] = None,
|
192
|
-
check: bool = False, # noqa
|
193
|
-
capture_output: ta.Optional[bool] = None,
|
194
|
-
**kwargs: ta.Any,
|
195
|
-
) -> AsyncioSubprocessOutput:
|
196
|
-
if capture_output:
|
197
|
-
kwargs.setdefault('stdout', subprocess.PIPE)
|
198
|
-
kwargs.setdefault('stderr', subprocess.PIPE)
|
199
|
-
|
200
|
-
args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
|
201
|
-
|
202
|
-
proc: asyncio.subprocess.Process
|
203
|
-
async with asyncio_subprocess_popen(*args, **kwargs) as proc:
|
204
|
-
stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
|
205
|
-
|
206
|
-
if check and proc.returncode:
|
207
|
-
raise subprocess.CalledProcessError(
|
208
|
-
proc.returncode,
|
209
|
-
args,
|
210
|
-
output=stdout,
|
211
|
-
stderr=stderr,
|
212
|
-
)
|
133
|
+
##
|
213
134
|
|
214
|
-
return AsyncioSubprocessOutput(
|
215
|
-
proc,
|
216
|
-
stdout,
|
217
|
-
stderr,
|
218
|
-
)
|
219
135
|
|
136
|
+
class AsyncioSubprocesses(AbstractSubprocesses):
|
137
|
+
async def communicate(
|
138
|
+
self,
|
139
|
+
proc: asyncio.subprocess.Process,
|
140
|
+
input: ta.Any = None, # noqa
|
141
|
+
timeout: ta.Optional[float] = None,
|
142
|
+
) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
|
143
|
+
return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
|
220
144
|
|
221
|
-
|
145
|
+
#
|
222
146
|
|
147
|
+
@contextlib.asynccontextmanager
|
148
|
+
async def popen(
|
149
|
+
self,
|
150
|
+
*cmd: str,
|
151
|
+
shell: bool = False,
|
152
|
+
timeout: ta.Optional[float] = None,
|
153
|
+
**kwargs: ta.Any,
|
154
|
+
) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
|
155
|
+
fac: ta.Any
|
156
|
+
if shell:
|
157
|
+
fac = functools.partial(
|
158
|
+
asyncio.create_subprocess_shell,
|
159
|
+
check.single(cmd),
|
160
|
+
)
|
161
|
+
else:
|
162
|
+
fac = functools.partial(
|
163
|
+
asyncio.create_subprocess_exec,
|
164
|
+
*cmd,
|
165
|
+
)
|
223
166
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
timeout: ta.Optional[float] = None,
|
229
|
-
**kwargs: ta.Any,
|
230
|
-
) -> None:
|
231
|
-
await asyncio_subprocess_run(
|
232
|
-
*args,
|
233
|
-
stdout=stdout,
|
234
|
-
input=input,
|
235
|
-
timeout=timeout,
|
236
|
-
check=True,
|
237
|
-
**kwargs,
|
238
|
-
)
|
239
|
-
|
240
|
-
|
241
|
-
async def asyncio_subprocess_check_output(
|
242
|
-
*args: str,
|
243
|
-
input: ta.Any = None, # noqa
|
244
|
-
timeout: ta.Optional[float] = None,
|
245
|
-
**kwargs: ta.Any,
|
246
|
-
) -> bytes:
|
247
|
-
out = await asyncio_subprocess_run(
|
248
|
-
*args,
|
249
|
-
stdout=asyncio.subprocess.PIPE,
|
250
|
-
input=input,
|
251
|
-
timeout=timeout,
|
252
|
-
check=True,
|
253
|
-
**kwargs,
|
254
|
-
)
|
255
|
-
|
256
|
-
return check.not_none(out.stdout)
|
257
|
-
|
258
|
-
|
259
|
-
async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
|
260
|
-
return (await asyncio_subprocess_check_output(*args, **kwargs)).decode().strip()
|
167
|
+
with self.prepare_and_wrap( *cmd, shell=shell, **kwargs) as (cmd, kwargs): # noqa
|
168
|
+
proc: asyncio.subprocess.Process = await fac(**kwargs)
|
169
|
+
try:
|
170
|
+
yield proc
|
261
171
|
|
172
|
+
finally:
|
173
|
+
await asyncio_maybe_timeout(proc.wait(), timeout)
|
262
174
|
|
263
|
-
|
175
|
+
#
|
264
176
|
|
177
|
+
@dc.dataclass(frozen=True)
|
178
|
+
class RunOutput:
|
179
|
+
proc: asyncio.subprocess.Process
|
180
|
+
stdout: ta.Optional[bytes]
|
181
|
+
stderr: ta.Optional[bytes]
|
265
182
|
|
266
|
-
async def
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
if
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
async
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
return
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
183
|
+
async def run(
|
184
|
+
self,
|
185
|
+
*cmd: str,
|
186
|
+
input: ta.Any = None, # noqa
|
187
|
+
timeout: ta.Optional[float] = None,
|
188
|
+
check: bool = False, # noqa
|
189
|
+
capture_output: ta.Optional[bool] = None,
|
190
|
+
**kwargs: ta.Any,
|
191
|
+
) -> RunOutput:
|
192
|
+
if capture_output:
|
193
|
+
kwargs.setdefault('stdout', subprocess.PIPE)
|
194
|
+
kwargs.setdefault('stderr', subprocess.PIPE)
|
195
|
+
|
196
|
+
proc: asyncio.subprocess.Process
|
197
|
+
async with self.popen(*cmd, **kwargs) as proc:
|
198
|
+
stdout, stderr = await self.communicate(proc, input, timeout)
|
199
|
+
|
200
|
+
if check and proc.returncode:
|
201
|
+
raise subprocess.CalledProcessError(
|
202
|
+
proc.returncode,
|
203
|
+
cmd,
|
204
|
+
output=stdout,
|
205
|
+
stderr=stderr,
|
206
|
+
)
|
207
|
+
|
208
|
+
return self.RunOutput(
|
209
|
+
proc,
|
210
|
+
stdout,
|
211
|
+
stderr,
|
212
|
+
)
|
213
|
+
|
214
|
+
#
|
215
|
+
|
216
|
+
async def check_call(
|
217
|
+
self,
|
218
|
+
*cmd: str,
|
219
|
+
stdout: ta.Any = sys.stderr,
|
220
|
+
**kwargs: ta.Any,
|
221
|
+
) -> None:
|
222
|
+
with self.prepare_and_wrap(*cmd, stdout=stdout, check=True, **kwargs) as (cmd, kwargs): # noqa
|
223
|
+
await self.run(*cmd, **kwargs)
|
224
|
+
|
225
|
+
async def check_output(
|
226
|
+
self,
|
227
|
+
*cmd: str,
|
228
|
+
**kwargs: ta.Any,
|
229
|
+
) -> bytes:
|
230
|
+
with self.prepare_and_wrap(*cmd, stdout=subprocess.PIPE, check=True, **kwargs) as (cmd, kwargs): # noqa
|
231
|
+
return check.not_none((await self.run(*cmd, **kwargs)).stdout)
|
232
|
+
|
233
|
+
async def check_output_str(
|
234
|
+
self,
|
235
|
+
*cmd: str,
|
236
|
+
**kwargs: ta.Any,
|
237
|
+
) -> str:
|
238
|
+
return (await self.check_output(*cmd, **kwargs)).decode().strip()
|
239
|
+
|
240
|
+
#
|
241
|
+
|
242
|
+
async def try_call(
|
243
|
+
self,
|
244
|
+
*cmd: str,
|
245
|
+
**kwargs: ta.Any,
|
246
|
+
) -> bool:
|
247
|
+
if isinstance(await self.async_try_fn(self.check_call, *cmd, **kwargs), Exception):
|
248
|
+
return False
|
249
|
+
else:
|
250
|
+
return True
|
251
|
+
|
252
|
+
async def try_output(
|
253
|
+
self,
|
254
|
+
*cmd: str,
|
255
|
+
**kwargs: ta.Any,
|
256
|
+
) -> ta.Optional[bytes]:
|
257
|
+
if isinstance(ret := await self.async_try_fn(self.check_output, *cmd, **kwargs), Exception):
|
258
|
+
return None
|
259
|
+
else:
|
260
|
+
return ret
|
261
|
+
|
262
|
+
async def try_output_str(
|
263
|
+
self,
|
264
|
+
*cmd: str,
|
265
|
+
**kwargs: ta.Any,
|
266
|
+
) -> ta.Optional[str]:
|
267
|
+
if (ret := await self.try_output(*cmd, **kwargs)) is None:
|
268
|
+
return None
|
269
|
+
else:
|
270
|
+
return ret.decode().strip()
|
310
271
|
|
311
272
|
|
312
|
-
|
313
|
-
out = await asyncio_subprocess_try_output(*args, **kwargs)
|
314
|
-
return out.decode().strip() if out is not None else None
|
273
|
+
asyncio_subprocesses = AsyncioSubprocesses()
|
omlish/lite/cached.py
CHANGED
@@ -35,8 +35,8 @@ class _CachedNullary(_AbstractCachedNullary):
|
|
35
35
|
return self._value
|
36
36
|
|
37
37
|
|
38
|
-
def cached_nullary(fn
|
39
|
-
return _CachedNullary(fn)
|
38
|
+
def cached_nullary(fn: CallableT) -> CallableT:
|
39
|
+
return _CachedNullary(fn) # type: ignore
|
40
40
|
|
41
41
|
|
42
42
|
def static_init(fn: CallableT) -> CallableT:
|
omlish/lite/marshal.py
CHANGED
omlish/lite/subprocesses.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
2
3
|
import contextlib
|
3
4
|
import logging
|
4
5
|
import os
|
@@ -8,8 +9,8 @@ import sys
|
|
8
9
|
import time
|
9
10
|
import typing as ta
|
10
11
|
|
11
|
-
from .logs import log
|
12
|
-
from .runtime import is_debugger_attached
|
12
|
+
from omlish.lite.logs import log
|
13
|
+
from omlish.lite.runtime import is_debugger_attached
|
13
14
|
|
14
15
|
|
15
16
|
T = ta.TypeVar('T')
|
@@ -32,165 +33,219 @@ SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
|
|
32
33
|
_SUBPROCESS_SHELL_WRAP_EXECS = False
|
33
34
|
|
34
35
|
|
35
|
-
def subprocess_shell_wrap_exec(*
|
36
|
-
return ('sh', '-c', ' '.join(map(shlex.quote,
|
36
|
+
def subprocess_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
|
37
|
+
return ('sh', '-c', ' '.join(map(shlex.quote, cmd)))
|
37
38
|
|
38
39
|
|
39
|
-
def subprocess_maybe_shell_wrap_exec(*
|
40
|
+
def subprocess_maybe_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
|
40
41
|
if _SUBPROCESS_SHELL_WRAP_EXECS or is_debugger_attached():
|
41
|
-
return subprocess_shell_wrap_exec(*
|
42
|
+
return subprocess_shell_wrap_exec(*cmd)
|
42
43
|
else:
|
43
|
-
return
|
44
|
-
|
45
|
-
|
46
|
-
def prepare_subprocess_invocation(
|
47
|
-
*args: str,
|
48
|
-
env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
49
|
-
extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
50
|
-
quiet: bool = False,
|
51
|
-
shell: bool = False,
|
52
|
-
**kwargs: ta.Any,
|
53
|
-
) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
|
54
|
-
log.debug('prepare_subprocess_invocation: args=%r', args)
|
55
|
-
if extra_env:
|
56
|
-
log.debug('prepare_subprocess_invocation: extra_env=%r', extra_env)
|
57
|
-
|
58
|
-
if extra_env:
|
59
|
-
env = {**(env if env is not None else os.environ), **extra_env}
|
60
|
-
|
61
|
-
if quiet and 'stderr' not in kwargs:
|
62
|
-
if not log.isEnabledFor(logging.DEBUG):
|
63
|
-
kwargs['stderr'] = subprocess.DEVNULL
|
64
|
-
|
65
|
-
if not shell:
|
66
|
-
args = subprocess_maybe_shell_wrap_exec(*args)
|
67
|
-
|
68
|
-
return args, dict(
|
69
|
-
env=env,
|
70
|
-
shell=shell,
|
71
|
-
**kwargs,
|
72
|
-
)
|
73
|
-
|
74
|
-
|
75
|
-
##
|
76
|
-
|
77
|
-
|
78
|
-
@contextlib.contextmanager
|
79
|
-
def subprocess_common_context(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
|
80
|
-
start_time = time.time()
|
81
|
-
try:
|
82
|
-
log.debug('subprocess_common_context.try: args=%r', args)
|
83
|
-
yield
|
84
|
-
|
85
|
-
except Exception as exc: # noqa
|
86
|
-
log.debug('subprocess_common_context.except: exc=%r', exc)
|
87
|
-
raise
|
88
|
-
|
89
|
-
finally:
|
90
|
-
end_time = time.time()
|
91
|
-
elapsed_s = end_time - start_time
|
92
|
-
log.debug('subprocess_common_context.finally: elapsed_s=%f args=%r', elapsed_s, args)
|
44
|
+
return cmd
|
93
45
|
|
94
46
|
|
95
47
|
##
|
96
48
|
|
97
49
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
**kwargs: ta.Any,
|
50
|
+
def subprocess_close(
|
51
|
+
proc: subprocess.Popen,
|
52
|
+
timeout: ta.Optional[float] = None,
|
102
53
|
) -> None:
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
**kwargs: ta.Any,
|
111
|
-
) -> bytes:
|
112
|
-
args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
|
113
|
-
with subprocess_common_context(*args, **kwargs):
|
114
|
-
return subprocess.check_output(args, **kwargs)
|
115
|
-
|
54
|
+
# TODO: terminate, sleep, kill
|
55
|
+
if proc.stdout:
|
56
|
+
proc.stdout.close()
|
57
|
+
if proc.stderr:
|
58
|
+
proc.stderr.close()
|
59
|
+
if proc.stdin:
|
60
|
+
proc.stdin.close()
|
116
61
|
|
117
|
-
|
118
|
-
return subprocess_check_output(*args, **kwargs).decode().strip()
|
62
|
+
proc.wait(timeout)
|
119
63
|
|
120
64
|
|
121
65
|
##
|
122
66
|
|
123
67
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
) ->
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
68
|
+
class AbstractSubprocesses(abc.ABC): # noqa
|
69
|
+
DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = log
|
70
|
+
|
71
|
+
def __init__(
|
72
|
+
self,
|
73
|
+
*,
|
74
|
+
log: ta.Optional[logging.Logger] = None,
|
75
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
76
|
+
) -> None:
|
77
|
+
super().__init__()
|
78
|
+
|
79
|
+
self._log = log if log is not None else self.DEFAULT_LOGGER
|
80
|
+
self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
|
81
|
+
|
82
|
+
#
|
83
|
+
|
84
|
+
def prepare_args(
|
85
|
+
self,
|
86
|
+
*cmd: str,
|
87
|
+
env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
88
|
+
extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
89
|
+
quiet: bool = False,
|
90
|
+
shell: bool = False,
|
91
|
+
**kwargs: ta.Any,
|
92
|
+
) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
|
93
|
+
if self._log:
|
94
|
+
self._log.debug('Subprocesses.prepare_args: cmd=%r', cmd)
|
95
|
+
if extra_env:
|
96
|
+
self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
|
97
|
+
|
98
|
+
if extra_env:
|
99
|
+
env = {**(env if env is not None else os.environ), **extra_env}
|
100
|
+
|
101
|
+
if quiet and 'stderr' not in kwargs:
|
102
|
+
if self._log and not self._log.isEnabledFor(logging.DEBUG):
|
103
|
+
kwargs['stderr'] = subprocess.DEVNULL
|
104
|
+
|
105
|
+
if not shell:
|
106
|
+
cmd = subprocess_maybe_shell_wrap_exec(*cmd)
|
107
|
+
|
108
|
+
return cmd, dict(
|
109
|
+
env=env,
|
110
|
+
shell=shell,
|
153
111
|
**kwargs,
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
112
|
+
)
|
113
|
+
|
114
|
+
@contextlib.contextmanager
|
115
|
+
def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
|
116
|
+
start_time = time.time()
|
117
|
+
try:
|
118
|
+
if self._log:
|
119
|
+
self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
|
120
|
+
yield
|
121
|
+
|
122
|
+
except Exception as exc: # noqa
|
123
|
+
if self._log:
|
124
|
+
self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
|
125
|
+
raise
|
126
|
+
|
127
|
+
finally:
|
128
|
+
end_time = time.time()
|
129
|
+
elapsed_s = end_time - start_time
|
130
|
+
if self._log:
|
131
|
+
self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
|
132
|
+
|
133
|
+
@contextlib.contextmanager
|
134
|
+
def prepare_and_wrap(
|
135
|
+
self,
|
136
|
+
*cmd: ta.Any,
|
137
|
+
**kwargs: ta.Any,
|
138
|
+
) -> ta.Iterator[ta.Tuple[
|
139
|
+
ta.Tuple[ta.Any, ...],
|
140
|
+
ta.Dict[str, ta.Any],
|
141
|
+
]]:
|
142
|
+
cmd, kwargs = self.prepare_args(*cmd, **kwargs)
|
143
|
+
with self.wrap_call(*cmd, **kwargs):
|
144
|
+
yield cmd, kwargs
|
145
|
+
|
146
|
+
#
|
147
|
+
|
148
|
+
DEFAULT_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
|
149
|
+
FileNotFoundError,
|
150
|
+
subprocess.CalledProcessError,
|
151
|
+
)
|
175
152
|
|
176
|
-
def
|
177
|
-
|
178
|
-
|
153
|
+
def try_fn(
|
154
|
+
self,
|
155
|
+
fn: ta.Callable[..., T],
|
156
|
+
*cmd: str,
|
157
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
158
|
+
**kwargs: ta.Any,
|
159
|
+
) -> ta.Union[T, Exception]:
|
160
|
+
if try_exceptions is None:
|
161
|
+
try_exceptions = self._try_exceptions
|
162
|
+
|
163
|
+
try:
|
164
|
+
return fn(*cmd, **kwargs)
|
165
|
+
|
166
|
+
except try_exceptions as e: # noqa
|
167
|
+
if self._log and self._log.isEnabledFor(logging.DEBUG):
|
168
|
+
self._log.exception('command failed')
|
169
|
+
return e
|
170
|
+
|
171
|
+
async def async_try_fn(
|
172
|
+
self,
|
173
|
+
fn: ta.Callable[..., ta.Awaitable[T]],
|
174
|
+
*cmd: ta.Any,
|
175
|
+
try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
|
176
|
+
**kwargs: ta.Any,
|
177
|
+
) -> ta.Union[T, Exception]:
|
178
|
+
if try_exceptions is None:
|
179
|
+
try_exceptions = self._try_exceptions
|
180
|
+
|
181
|
+
try:
|
182
|
+
return await fn(*cmd, **kwargs)
|
183
|
+
|
184
|
+
except try_exceptions as e: # noqa
|
185
|
+
if self._log and self._log.isEnabledFor(logging.DEBUG):
|
186
|
+
self._log.exception('command failed')
|
187
|
+
return e
|
179
188
|
|
180
189
|
|
181
190
|
##
|
182
191
|
|
183
192
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
193
|
+
class Subprocesses(AbstractSubprocesses):
|
194
|
+
def check_call(
|
195
|
+
self,
|
196
|
+
*cmd: str,
|
197
|
+
stdout: ta.Any = sys.stderr,
|
198
|
+
**kwargs: ta.Any,
|
199
|
+
) -> None:
|
200
|
+
with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
|
201
|
+
subprocess.check_call(cmd, **kwargs)
|
202
|
+
|
203
|
+
def check_output(
|
204
|
+
self,
|
205
|
+
*cmd: str,
|
206
|
+
**kwargs: ta.Any,
|
207
|
+
) -> bytes:
|
208
|
+
with self.prepare_and_wrap(*cmd, **kwargs) as (cmd, kwargs): # noqa
|
209
|
+
return subprocess.check_output(cmd, **kwargs)
|
210
|
+
|
211
|
+
def check_output_str(
|
212
|
+
self,
|
213
|
+
*cmd: str,
|
214
|
+
**kwargs: ta.Any,
|
215
|
+
) -> str:
|
216
|
+
return self.check_output(*cmd, **kwargs).decode().strip()
|
217
|
+
|
218
|
+
#
|
219
|
+
|
220
|
+
def try_call(
|
221
|
+
self,
|
222
|
+
*cmd: str,
|
223
|
+
**kwargs: ta.Any,
|
224
|
+
) -> bool:
|
225
|
+
if isinstance(self.try_fn(self.check_call, *cmd, **kwargs), Exception):
|
226
|
+
return False
|
227
|
+
else:
|
228
|
+
return True
|
229
|
+
|
230
|
+
def try_output(
|
231
|
+
self,
|
232
|
+
*cmd: str,
|
233
|
+
**kwargs: ta.Any,
|
234
|
+
) -> ta.Optional[bytes]:
|
235
|
+
if isinstance(ret := self.try_fn(self.check_output, *cmd, **kwargs), Exception):
|
236
|
+
return None
|
237
|
+
else:
|
238
|
+
return ret
|
239
|
+
|
240
|
+
def try_output_str(
|
241
|
+
self,
|
242
|
+
*cmd: str,
|
243
|
+
**kwargs: ta.Any,
|
244
|
+
) -> ta.Optional[str]:
|
245
|
+
if (ret := self.try_output(*cmd, **kwargs)) is None:
|
246
|
+
return None
|
247
|
+
else:
|
248
|
+
return ret.decode().strip()
|
249
|
+
|
250
|
+
|
251
|
+
subprocesses = Subprocesses()
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=RX24SRc6DCEg77PUVnaXOKCWa5TF_c9RQJdGIf7gl9c,1135
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=Gp7-GXOxzTN9ee-T1I3svA-Tc_rF24tMP_-u8t9ApaI,3409
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
5
5
|
omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
|
@@ -75,7 +75,7 @@ omlish/antlr/_runtime/xpath/XPathLexer.py,sha256=xFtdr4ZXMZxb2dnB_ggWyhvlQiC7RXQ
|
|
75
75
|
omlish/antlr/_runtime/xpath/__init__.py,sha256=lMd_BbXYdlDhZQN_q0TKN978XW5G0pq618F0NaLkpFE,71
|
76
76
|
omlish/argparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
77
|
omlish/argparse/all.py,sha256=EfUSf27vFWqa4Q93AycU5YRsrHt-Nx3pU3uNVapb-EE,1054
|
78
|
-
omlish/argparse/cli.py,sha256=
|
78
|
+
omlish/argparse/cli.py,sha256=Nd16RNNABOd9UjfLGtJrDOYkIs5uv1BnoqvJdQ2Ipc8,8273
|
79
79
|
omlish/asyncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
80
80
|
omlish/asyncs/all.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
|
81
81
|
omlish/asyncs/anyio.py,sha256=gfpx-D8QGmUfhnQxHEaHXcAP8zSMQjcGw4COFTGNnHI,8021
|
@@ -333,13 +333,13 @@ omlish/lifecycles/manager.py,sha256=Au66KaO-fI-SEJALaPUJsCHYW2GE20xextk1wKn2BEU,
|
|
333
333
|
omlish/lifecycles/states.py,sha256=zqMOU2ZU-MDNnWuwauM3_anIAiXM8LoBDElDEraptFg,1292
|
334
334
|
omlish/lifecycles/transitions.py,sha256=qQtFby-h4VzbvgaUqT2NnbNumlcOx9FVVADP9t83xj4,1939
|
335
335
|
omlish/lite/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
336
|
-
omlish/lite/cached.py,sha256=
|
336
|
+
omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
|
337
337
|
omlish/lite/check.py,sha256=KvcO86LqWlh2j4ORaZXRR4FM8fFb7kUkNqq3lTs0Ta0,12821
|
338
338
|
omlish/lite/contextmanagers.py,sha256=m9JO--p7L7mSl4cycXysH-1AO27weDKjP3DZG61cwwM,1683
|
339
339
|
omlish/lite/inject.py,sha256=729Qi0TLbQgBtkvx97q1EUMe73VFYA1hu4woXkOTcwM,23572
|
340
340
|
omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
|
341
341
|
omlish/lite/logs.py,sha256=1pcGu0ekhVCcLUckLSP16VccnAoprjtl5Vkdfm7y1Wg,6184
|
342
|
-
omlish/lite/marshal.py,sha256=
|
342
|
+
omlish/lite/marshal.py,sha256=u5n43bEbct1ps8oIR7wjFCSWeyOhfHAF7LxUjELh8Jk,13830
|
343
343
|
omlish/lite/maybes.py,sha256=7OlHJ8Q2r4wQ-aRbZSlJY7x0e8gDvufFdlohGEIJ3P4,833
|
344
344
|
omlish/lite/pycharm.py,sha256=pUOJevrPClSqTCEOkQBO11LKX2003tfDcp18a03QFrc,1163
|
345
345
|
omlish/lite/reflect.py,sha256=ad_ya_zZJOQB8HoNjs9yc66R54zgflwJVPJqiBXMzqA,1681
|
@@ -349,10 +349,10 @@ omlish/lite/secrets.py,sha256=3Mz3V2jf__XU9qNHcH56sBSw95L3U2UPL24bjvobG0c,816
|
|
349
349
|
omlish/lite/socket.py,sha256=7OYgkXTcQv0wq7TQuLnl9y6dJA1ZT6Vbc1JH59QlxgY,1792
|
350
350
|
omlish/lite/socketserver.py,sha256=doTXIctu_6c8XneFtzPFVG_Wq6xVmA3p9ymut8IvBoU,1586
|
351
351
|
omlish/lite/strings.py,sha256=QURcE4-1pKVW8eT_5VCJpXaHDWR2dW2pYOChTJnZDiQ,1504
|
352
|
-
omlish/lite/subprocesses.py,sha256=
|
352
|
+
omlish/lite/subprocesses.py,sha256=fN8Hu1cDhu9PoYlfwYyIsMpeznOzisIU_3eKcYcnARs,6700
|
353
353
|
omlish/lite/typing.py,sha256=U3-JaEnkDSYxK4tsu_MzUn3RP6qALBe5FXQXpD-licE,1090
|
354
354
|
omlish/lite/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
355
|
-
omlish/lite/asyncio/subprocesses.py,sha256=
|
355
|
+
omlish/lite/asyncio/subprocesses.py,sha256=pTcSbBDM6rpPKnjl8YqfdOhThDSalDcZHuvaCzYF07o,7729
|
356
356
|
omlish/logs/__init__.py,sha256=FbOyAW-lGH8gyBlSVArwljdYAU6RnwZLI5LwAfuNnrk,438
|
357
357
|
omlish/logs/abc.py,sha256=ho4ABKYMKX-V7g4sp1BByuOLzslYzLlQ0MESmjEpT-o,8005
|
358
358
|
omlish/logs/configs.py,sha256=EE0jlNaXJbGnM7V-y4xS5VwyTBSTzFzc0BYaVjg0JmA,1283
|
@@ -520,9 +520,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
|
|
520
520
|
omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
|
521
521
|
omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
|
522
522
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
523
|
-
omlish-0.0.0.
|
524
|
-
omlish-0.0.0.
|
525
|
-
omlish-0.0.0.
|
526
|
-
omlish-0.0.0.
|
527
|
-
omlish-0.0.0.
|
528
|
-
omlish-0.0.0.
|
523
|
+
omlish-0.0.0.dev157.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
524
|
+
omlish-0.0.0.dev157.dist-info/METADATA,sha256=7EjWfGRPNiPJlMhOs3pO4xT7Le7y2kO4WqbSIZFX-Gc,4264
|
525
|
+
omlish-0.0.0.dev157.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
526
|
+
omlish-0.0.0.dev157.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
527
|
+
omlish-0.0.0.dev157.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
528
|
+
omlish-0.0.0.dev157.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|