omlish 0.0.0.dev154__py3-none-any.whl → 0.0.0.dev156__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.
@@ -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()