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.
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):