omlish 0.0.0.dev155__py3-none-any.whl → 0.0.0.dev156__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev155'
2
- __revision__ = '7187ecad3907bd95f19a85608e9cbd7b9a103920'
1
+ __version__ = '0.0.0.dev156'
2
+ __revision__ = '594c80ed50a0c5515f35cac4b31a4ed482993a4b'
3
3
 
4
4
 
5
5
  #
@@ -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 DEFAULT_SUBPROCESS_TRY_EXCEPTIONS
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
- async def asyncio_subprocess_communicate(
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
- async def asyncio_subprocess_check_call(
225
- *args: str,
226
- stdout: ta.Any = sys.stderr,
227
- input: ta.Any = None, # noqa
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 _asyncio_subprocess_try_run(
267
- fn: ta.Callable[..., ta.Awaitable[T]],
268
- *args: ta.Any,
269
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
270
- **kwargs: ta.Any,
271
- ) -> ta.Union[T, Exception]:
272
- try:
273
- return await fn(*args, **kwargs)
274
- except try_exceptions as e: # noqa
275
- if log.isEnabledFor(logging.DEBUG):
276
- log.exception('command failed')
277
- return e
278
-
279
-
280
- async def asyncio_subprocess_try_call(
281
- *args: str,
282
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
283
- **kwargs: ta.Any,
284
- ) -> bool:
285
- if isinstance(await _asyncio_subprocess_try_run(
286
- asyncio_subprocess_check_call,
287
- *args,
288
- try_exceptions=try_exceptions,
289
- **kwargs,
290
- ), Exception):
291
- return False
292
- else:
293
- return True
294
-
295
-
296
- async def asyncio_subprocess_try_output(
297
- *args: str,
298
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
299
- **kwargs: ta.Any,
300
- ) -> ta.Optional[bytes]:
301
- if isinstance(ret := await _asyncio_subprocess_try_run(
302
- asyncio_subprocess_check_output,
303
- *args,
304
- try_exceptions=try_exceptions,
305
- **kwargs,
306
- ), Exception):
307
- return None
308
- else:
309
- return ret
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
- async def asyncio_subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
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()
@@ -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(*args: str) -> ta.Tuple[str, ...]:
36
- return ('sh', '-c', ' '.join(map(shlex.quote, args)))
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(*args: str) -> ta.Tuple[str, ...]:
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(*args)
42
+ return subprocess_shell_wrap_exec(*cmd)
42
43
  else:
43
- return args
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 subprocess_check_call(
99
- *args: str,
100
- stdout: ta.Any = sys.stderr,
101
- **kwargs: ta.Any,
50
+ def subprocess_close(
51
+ proc: subprocess.Popen,
52
+ timeout: ta.Optional[float] = None,
102
53
  ) -> None:
103
- args, kwargs = prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
104
- with subprocess_common_context(*args, **kwargs):
105
- return subprocess.check_call(args, **kwargs) # type: ignore
106
-
107
-
108
- def subprocess_check_output(
109
- *args: str,
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
- def subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
118
- return subprocess_check_output(*args, **kwargs).decode().strip()
62
+ proc.wait(timeout)
119
63
 
120
64
 
121
65
  ##
122
66
 
123
67
 
124
- DEFAULT_SUBPROCESS_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
125
- FileNotFoundError,
126
- subprocess.CalledProcessError,
127
- )
128
-
129
-
130
- def _subprocess_try_run(
131
- fn: ta.Callable[..., T],
132
- *args: ta.Any,
133
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
134
- **kwargs: ta.Any,
135
- ) -> ta.Union[T, Exception]:
136
- try:
137
- return fn(*args, **kwargs)
138
- except try_exceptions as e: # noqa
139
- if log.isEnabledFor(logging.DEBUG):
140
- log.exception('command failed')
141
- return e
142
-
143
-
144
- def subprocess_try_call(
145
- *args: str,
146
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
147
- **kwargs: ta.Any,
148
- ) -> bool:
149
- if isinstance(_subprocess_try_run(
150
- subprocess_check_call,
151
- *args,
152
- try_exceptions=try_exceptions,
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
- ), Exception):
155
- return False
156
- else:
157
- return True
158
-
159
-
160
- def subprocess_try_output(
161
- *args: str,
162
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
163
- **kwargs: ta.Any,
164
- ) -> ta.Optional[bytes]:
165
- if isinstance(ret := _subprocess_try_run(
166
- subprocess_check_output,
167
- *args,
168
- try_exceptions=try_exceptions,
169
- **kwargs,
170
- ), Exception):
171
- return None
172
- else:
173
- return ret
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 subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
177
- out = subprocess_try_output(*args, **kwargs)
178
- return out.decode().strip() if out is not None else None
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
- def subprocess_close(
185
- proc: subprocess.Popen,
186
- timeout: ta.Optional[float] = None,
187
- ) -> None:
188
- # TODO: terminate, sleep, kill
189
- if proc.stdout:
190
- proc.stdout.close()
191
- if proc.stderr:
192
- proc.stderr.close()
193
- if proc.stdin:
194
- proc.stdin.close()
195
-
196
- proc.wait(timeout)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev155
3
+ Version: 0.0.0.dev156
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=RX24SRc6DCEg77PUVnaXOKCWa5TF_c9RQJdGIf7gl9c,1135
2
- omlish/__about__.py,sha256=TTGSgQiaw8RlS3TEaNm9PX3osMhDFRCTCzGdQmtU5dQ,3409
2
+ omlish/__about__.py,sha256=pJXW0d9gycr4V9VlmGz-WLKw7S0ffYStbINM8F0lblQ,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
@@ -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=iN-HX44g9uxkZ7HII2Upvkfjp7YK6qQuhPrBqM4Hnp0,4934
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=luHARTCKB5qoxwUln1LH_v-z8k7YTkf32b2rgV_qkMg,8634
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.dev155.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
524
- omlish-0.0.0.dev155.dist-info/METADATA,sha256=EZnz0auK1L6Br8TpcHXEE4T5zN79d2pJpORfJEZeFss,4264
525
- omlish-0.0.0.dev155.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
526
- omlish-0.0.0.dev155.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
527
- omlish-0.0.0.dev155.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
528
- omlish-0.0.0.dev155.dist-info/RECORD,,
523
+ omlish-0.0.0.dev156.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
524
+ omlish-0.0.0.dev156.dist-info/METADATA,sha256=_LbGRk-hTyGZJFGz-n_Jjjr65O9YGLkRqiLcCMEPLyE,4264
525
+ omlish-0.0.0.dev156.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
526
+ omlish-0.0.0.dev156.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
527
+ omlish-0.0.0.dev156.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
528
+ omlish-0.0.0.dev156.dist-info/RECORD,,