omlish 0.0.0.dev154__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.dev154'
2
- __revision__ = '148f19432b8d3a70543b9466386aa540fe95b6be'
1
+ __version__ = '0.0.0.dev156'
2
+ __revision__ = '594c80ed50a0c5515f35cac4b31a4ed482993a4b'
3
3
 
4
4
 
5
5
  #
@@ -0,0 +1 @@
1
+ # @omlish-lite
@@ -2,8 +2,8 @@
2
2
  import asyncio.base_subprocess
3
3
  import asyncio.subprocess
4
4
  import contextlib
5
+ import dataclasses as dc
5
6
  import functools
6
- import logging
7
7
  import subprocess
8
8
  import sys
9
9
  import typing as ta
@@ -11,9 +11,7 @@ import typing as ta
11
11
  from ...asyncs.asyncio.timeouts import asyncio_maybe_timeout
12
12
  from ..check import check
13
13
  from ..logs import log
14
- from ..subprocesses import DEFAULT_SUBPROCESS_TRY_EXCEPTIONS
15
- from ..subprocesses import prepare_subprocess_invocation
16
- from ..subprocesses import subprocess_common_context
14
+ from ..subprocesses import AbstractSubprocesses
17
15
 
18
16
 
19
17
  T = ta.TypeVar('T')
@@ -22,43 +20,6 @@ T = ta.TypeVar('T')
22
20
  ##
23
21
 
24
22
 
25
- @contextlib.asynccontextmanager
26
- async def asyncio_subprocess_popen(
27
- *cmd: str,
28
- shell: bool = False,
29
- timeout: ta.Optional[float] = None,
30
- **kwargs: ta.Any,
31
- ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
32
- fac: ta.Any
33
- if shell:
34
- fac = functools.partial(
35
- asyncio.create_subprocess_shell,
36
- check.single(cmd),
37
- )
38
- else:
39
- fac = functools.partial(
40
- asyncio.create_subprocess_exec,
41
- *cmd,
42
- )
43
-
44
- with subprocess_common_context(
45
- *cmd,
46
- shell=shell,
47
- timeout=timeout,
48
- **kwargs,
49
- ):
50
- proc: asyncio.subprocess.Process
51
- proc = await fac(**kwargs)
52
- try:
53
- yield proc
54
-
55
- finally:
56
- await asyncio_maybe_timeout(proc.wait(), timeout)
57
-
58
-
59
- ##
60
-
61
-
62
23
  class AsyncioProcessCommunicator:
63
24
  def __init__(
64
25
  self,
@@ -169,134 +130,144 @@ class AsyncioProcessCommunicator:
169
130
  return await asyncio_maybe_timeout(self._communicate(input), timeout)
170
131
 
171
132
 
172
- async def asyncio_subprocess_communicate(
173
- proc: asyncio.subprocess.Process,
174
- input: ta.Any = None, # noqa
175
- timeout: ta.Optional[float] = None,
176
- ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
177
- return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
178
-
179
-
180
- async def asyncio_subprocess_run(
181
- *args: str,
182
- input: ta.Any = None, # noqa
183
- timeout: ta.Optional[float] = None,
184
- check: bool = False, # noqa
185
- capture_output: ta.Optional[bool] = None,
186
- **kwargs: ta.Any,
187
- ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
188
- if capture_output:
189
- kwargs.setdefault('stdout', subprocess.PIPE)
190
- kwargs.setdefault('stderr', subprocess.PIPE)
191
-
192
- args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
193
-
194
- proc: asyncio.subprocess.Process
195
- async with asyncio_subprocess_popen(*args, **kwargs) as proc:
196
- stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
197
-
198
- if check and proc.returncode:
199
- raise subprocess.CalledProcessError(
200
- proc.returncode,
201
- args,
202
- output=stdout,
203
- stderr=stderr,
204
- )
133
+ ##
205
134
 
206
- return stdout, stderr
207
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
208
144
 
209
- ##
145
+ #
210
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
+ )
211
166
 
212
- async def asyncio_subprocess_check_call(
213
- *args: str,
214
- stdout: ta.Any = sys.stderr,
215
- input: ta.Any = None, # noqa
216
- timeout: ta.Optional[float] = None,
217
- **kwargs: ta.Any,
218
- ) -> None:
219
- _, _ = await asyncio_subprocess_run(
220
- *args,
221
- stdout=stdout,
222
- input=input,
223
- timeout=timeout,
224
- check=True,
225
- **kwargs,
226
- )
227
-
228
-
229
- async def asyncio_subprocess_check_output(
230
- *args: str,
231
- input: ta.Any = None, # noqa
232
- timeout: ta.Optional[float] = None,
233
- **kwargs: ta.Any,
234
- ) -> bytes:
235
- stdout, stderr = await asyncio_subprocess_run(
236
- *args,
237
- stdout=asyncio.subprocess.PIPE,
238
- input=input,
239
- timeout=timeout,
240
- check=True,
241
- **kwargs,
242
- )
243
-
244
- return check.not_none(stdout)
245
-
246
-
247
- async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
248
- 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
249
171
 
172
+ finally:
173
+ await asyncio_maybe_timeout(proc.wait(), timeout)
250
174
 
251
- ##
175
+ #
252
176
 
177
+ @dc.dataclass(frozen=True)
178
+ class RunOutput:
179
+ proc: asyncio.subprocess.Process
180
+ stdout: ta.Optional[bytes]
181
+ stderr: ta.Optional[bytes]
253
182
 
254
- async def _asyncio_subprocess_try_run(
255
- fn: ta.Callable[..., ta.Awaitable[T]],
256
- *args: ta.Any,
257
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
258
- **kwargs: ta.Any,
259
- ) -> ta.Union[T, Exception]:
260
- try:
261
- return await fn(*args, **kwargs)
262
- except try_exceptions as e: # noqa
263
- if log.isEnabledFor(logging.DEBUG):
264
- log.exception('command failed')
265
- return e
266
-
267
-
268
- async def asyncio_subprocess_try_call(
269
- *args: str,
270
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
271
- **kwargs: ta.Any,
272
- ) -> bool:
273
- if isinstance(await _asyncio_subprocess_try_run(
274
- asyncio_subprocess_check_call,
275
- *args,
276
- try_exceptions=try_exceptions,
277
- **kwargs,
278
- ), Exception):
279
- return False
280
- else:
281
- return True
282
-
283
-
284
- async def asyncio_subprocess_try_output(
285
- *args: str,
286
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
287
- **kwargs: ta.Any,
288
- ) -> ta.Optional[bytes]:
289
- if isinstance(ret := await _asyncio_subprocess_try_run(
290
- asyncio_subprocess_check_output,
291
- *args,
292
- try_exceptions=try_exceptions,
293
- **kwargs,
294
- ), Exception):
295
- return None
296
- else:
297
- 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()
298
271
 
299
272
 
300
- async def asyncio_subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
301
- out = await asyncio_subprocess_try_output(*args, **kwargs)
302
- return out.decode().strip() if out is not None else None
273
+ asyncio_subprocesses = AsyncioSubprocesses()
omlish/lite/check.py CHANGED
@@ -2,8 +2,6 @@
2
2
  """
3
3
  TODO:
4
4
  - def maybe(v: lang.Maybe[T])
5
- - patch / override lite.check ?
6
- - checker interface?
7
5
  """
8
6
  import collections
9
7
  import threading
@@ -61,3 +61,17 @@ def attr_setting(obj, attr, val, *, default=None): # noqa
61
61
  delattr(obj, attr)
62
62
  else:
63
63
  setattr(obj, attr, orig)
64
+
65
+
66
+ ##
67
+
68
+
69
+ class aclosing(contextlib.AbstractAsyncContextManager): # noqa
70
+ def __init__(self, thing):
71
+ self.thing = thing
72
+
73
+ async def __aenter__(self):
74
+ return self.thing
75
+
76
+ async def __aexit__(self, *exc_info):
77
+ await self.thing.aclose()
omlish/lite/marshal.py CHANGED
@@ -1,7 +1,8 @@
1
1
  """
2
2
  TODO:
3
3
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
4
- - nonstrict toggle
4
+ - namedtuple
5
+ - literals
5
6
  """
6
7
  # ruff: noqa: UP006 UP007
7
8
  import abc
@@ -23,6 +24,7 @@ from .reflect import deep_subclasses
23
24
  from .reflect import get_optional_alias_arg
24
25
  from .reflect import is_generic_alias
25
26
  from .reflect import is_union_alias
27
+ from .strings import snake_case
26
28
 
27
29
 
28
30
  T = ta.TypeVar('T')
@@ -314,14 +316,18 @@ class ObjMarshalerManager:
314
316
  ) -> ObjMarshaler:
315
317
  if isinstance(ty, type):
316
318
  if abc.ABC in ty.__bases__:
317
- return PolymorphicObjMarshaler.of([ # type: ignore
319
+ impls = [ity for ity in deep_subclasses(ty) if abc.ABC not in ity.__bases__] # type: ignore
320
+ if all(ity.__qualname__.endswith(ty.__name__) for ity in impls):
321
+ ins = {ity: snake_case(ity.__qualname__[:-len(ty.__name__)]) for ity in impls}
322
+ else:
323
+ ins = {ity: ity.__qualname__ for ity in impls}
324
+ return PolymorphicObjMarshaler.of([
318
325
  PolymorphicObjMarshaler.Impl(
319
326
  ity,
320
- ity.__qualname__,
327
+ itn,
321
328
  rec(ity),
322
329
  )
323
- for ity in deep_subclasses(ty)
324
- if abc.ABC not in ity.__bases__
330
+ for ity, itn in ins.items()
325
331
  ])
326
332
 
327
333
  if issubclass(ty, enum.Enum):