omlish 0.0.0.dev147__py3-none-any.whl → 0.0.0.dev149__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.dev147'
2
- __revision__ = 'd937c5e9830e9f6a67d18d09f079b827659e3339'
1
+ __version__ = '0.0.0.dev149'
2
+ __revision__ = '78e7c7e35dc2bc806db7fd199c1ce42b0f41cbe8'
3
3
 
4
4
 
5
5
  #
omlish/argparse/all.py ADDED
@@ -0,0 +1,45 @@
1
+ # ruff: noqa: I001
2
+ import argparse
3
+
4
+ from .cli import ( # noqa
5
+ ArgparseArg as Arg,
6
+ argparse_arg as arg,
7
+
8
+ ArgparseCommandFn as CommandFn,
9
+ ArgparseCommand as Command,
10
+ argparse_command as command,
11
+
12
+ ArgparseCli as Cli,
13
+ )
14
+
15
+
16
+ ##
17
+
18
+
19
+ SUPPRESS = argparse.SUPPRESS
20
+
21
+ OPTIONAL = argparse.OPTIONAL
22
+ ZERO_OR_MORE = argparse.ZERO_OR_MORE
23
+ ONE_OR_MORE = argparse.ONE_OR_MORE
24
+ PARSER = argparse.PARSER
25
+ REMAINDER = argparse.REMAINDER
26
+
27
+ HelpFormatter = argparse.HelpFormatter
28
+ RawDescriptionHelpFormatter = argparse.RawDescriptionHelpFormatter
29
+ RawTextHelpFormatter = argparse.RawTextHelpFormatter
30
+ ArgumentDefaultsHelpFormatter = argparse.ArgumentDefaultsHelpFormatter
31
+
32
+ MetavarTypeHelpFormatter = argparse.MetavarTypeHelpFormatter
33
+
34
+ ArgumentError = argparse.ArgumentError
35
+ ArgumentTypeError = argparse.ArgumentTypeError
36
+
37
+ Action = argparse.Action
38
+ BooleanOptionalAction = argparse.BooleanOptionalAction
39
+ SubParsersAction = argparse._SubParsersAction # noqa
40
+
41
+ FileType = argparse.FileType
42
+
43
+ Namespace = argparse.Namespace
44
+
45
+ ArgumentParser = argparse.ArgumentParser
@@ -1,3 +1,5 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
1
3
  """
2
4
  TODO:
3
5
  - default command
@@ -9,8 +11,11 @@ import functools
9
11
  import sys
10
12
  import typing as ta
11
13
 
12
- from . import c3
13
- from . import check
14
+ from ..lite.check import check_arg
15
+ from ..lite.check import check_isinstance
16
+ from ..lite.check import check_not_empty
17
+ from ..lite.check import check_not_in
18
+ from ..lite.check import check_not_isinstance
14
19
 
15
20
 
16
21
  T = ta.TypeVar('T')
@@ -19,43 +24,11 @@ T = ta.TypeVar('T')
19
24
  ##
20
25
 
21
26
 
22
- SUPPRESS = argparse.SUPPRESS
23
-
24
- OPTIONAL = argparse.OPTIONAL
25
- ZERO_OR_MORE = argparse.ZERO_OR_MORE
26
- ONE_OR_MORE = argparse.ONE_OR_MORE
27
- PARSER = argparse.PARSER
28
- REMAINDER = argparse.REMAINDER
29
-
30
- HelpFormatter = argparse.HelpFormatter
31
- RawDescriptionHelpFormatter = argparse.RawDescriptionHelpFormatter
32
- RawTextHelpFormatter = argparse.RawTextHelpFormatter
33
- ArgumentDefaultsHelpFormatter = argparse.ArgumentDefaultsHelpFormatter
34
-
35
- MetavarTypeHelpFormatter = argparse.MetavarTypeHelpFormatter
36
-
37
- ArgumentError = argparse.ArgumentError
38
- ArgumentTypeError = argparse.ArgumentTypeError
39
-
40
- Action = argparse.Action
41
- BooleanOptionalAction = argparse.BooleanOptionalAction
42
- SubParsersAction = argparse._SubParsersAction # noqa
43
-
44
- FileType = argparse.FileType
45
-
46
- Namespace = argparse.Namespace
47
-
48
- ArgumentParser = argparse.ArgumentParser
49
-
50
-
51
- ##
52
-
53
-
54
27
  @dc.dataclass(eq=False)
55
- class Arg:
28
+ class ArgparseArg:
56
29
  args: ta.Sequence[ta.Any]
57
30
  kwargs: ta.Mapping[str, ta.Any]
58
- dest: str | None = None
31
+ dest: ta.Optional[str] = None
59
32
 
60
33
  def __get__(self, instance, owner=None):
61
34
  if instance is None:
@@ -63,42 +36,42 @@ class Arg:
63
36
  return getattr(instance.args, self.dest) # type: ignore
64
37
 
65
38
 
66
- def arg(*args, **kwargs) -> Arg:
67
- return Arg(args, kwargs)
39
+ def argparse_arg(*args, **kwargs) -> ArgparseArg:
40
+ return ArgparseArg(args, kwargs)
68
41
 
69
42
 
70
43
  #
71
44
 
72
45
 
73
- CommandFn = ta.Callable[[], int | None]
46
+ ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
74
47
 
75
48
 
76
49
  @dc.dataclass(eq=False)
77
- class Command:
50
+ class ArgparseCommand:
78
51
  name: str
79
- fn: CommandFn
80
- args: ta.Sequence[Arg] = () # noqa
52
+ fn: ArgparseCommandFn
53
+ args: ta.Sequence[ArgparseArg] = () # noqa
81
54
 
82
- _: dc.KW_ONLY
55
+ # _: dc.KW_ONLY
83
56
 
84
- aliases: ta.Sequence[str] | None = None
85
- parent: ta.Optional['Command'] = None
57
+ aliases: ta.Optional[ta.Sequence[str]] = None
58
+ parent: ta.Optional['ArgparseCommand'] = None
86
59
  accepts_unknown: bool = False
87
60
 
88
61
  def __post_init__(self) -> None:
89
62
  def check_name(s: str) -> None:
90
- check.isinstance(s, str)
91
- check.not_in('_', s)
92
- check.not_empty(s)
63
+ check_isinstance(s, str)
64
+ check_not_in('_', s)
65
+ check_not_empty(s)
93
66
  check_name(self.name)
94
- check.not_isinstance(self.aliases, str)
67
+ check_not_isinstance(self.aliases, str)
95
68
  for a in self.aliases or []:
96
69
  check_name(a)
97
70
 
98
- check.callable(self.fn)
99
- check.arg(all(isinstance(a, Arg) for a in self.args))
100
- check.isinstance(self.parent, (Command, None))
101
- check.isinstance(self.accepts_unknown, bool)
71
+ check_arg(callable(self.fn))
72
+ check_arg(all(isinstance(a, ArgparseArg) for a in self.args))
73
+ check_isinstance(self.parent, (ArgparseCommand, type(None)))
74
+ check_isinstance(self.accepts_unknown, bool)
102
75
 
103
76
  functools.update_wrapper(self, self.fn)
104
77
 
@@ -107,25 +80,25 @@ class Command:
107
80
  return self
108
81
  return dc.replace(self, fn=self.fn.__get__(instance, owner)) # noqa
109
82
 
110
- def __call__(self, *args, **kwargs) -> int | None:
83
+ def __call__(self, *args, **kwargs) -> ta.Optional[int]:
111
84
  return self.fn(*args, **kwargs)
112
85
 
113
86
 
114
- def command(
115
- *args: Arg,
116
- name: str | None = None,
117
- aliases: ta.Iterable[str] | None = None,
118
- parent: Command | None = None,
87
+ def argparse_command(
88
+ *args: ArgparseArg,
89
+ name: ta.Optional[str] = None,
90
+ aliases: ta.Optional[ta.Iterable[str]] = None,
91
+ parent: ta.Optional[ArgparseCommand] = None,
119
92
  accepts_unknown: bool = False,
120
- ) -> ta.Any: # ta.Callable[[CommandFn], Command]: # FIXME
93
+ ) -> ta.Any: # ta.Callable[[ArgparseCommandFn], ArgparseCommand]: # FIXME
121
94
  for arg in args:
122
- check.isinstance(arg, Arg)
123
- check.isinstance(name, (str, None))
124
- check.isinstance(parent, (Command, None))
125
- check.not_isinstance(aliases, str)
95
+ check_isinstance(arg, ArgparseArg)
96
+ check_isinstance(name, (str, type(None)))
97
+ check_isinstance(parent, (ArgparseCommand, type(None)))
98
+ check_not_isinstance(aliases, str)
126
99
 
127
100
  def inner(fn):
128
- return Command(
101
+ return ArgparseCommand(
129
102
  (name if name is not None else fn.__name__).replace('_', '-'),
130
103
  fn,
131
104
  args,
@@ -140,7 +113,7 @@ def command(
140
113
  ##
141
114
 
142
115
 
143
- def get_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
116
+ def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
144
117
  if ann is str:
145
118
  return {}
146
119
  elif ann is int:
@@ -153,48 +126,51 @@ def get_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
153
126
  raise TypeError(ann)
154
127
 
155
128
 
156
- class _AnnotationBox:
157
-
129
+ class _ArgparseCliAnnotationBox:
158
130
  def __init__(self, annotations: ta.Mapping[str, ta.Any]) -> None:
159
131
  super().__init__()
160
132
  self.__annotations__ = annotations # type: ignore
161
133
 
162
134
 
163
- class _CliMeta(type):
135
+ class ArgparseCli:
136
+ def __init__(self, argv: ta.Optional[ta.Sequence[str]] = None) -> None:
137
+ super().__init__()
138
+
139
+ self._argv = argv if argv is not None else sys.argv[1:]
164
140
 
165
- def __new__(mcls, name: str, bases: ta.Sequence[type], namespace: ta.Mapping[str, ta.Any]) -> type:
166
- if not bases:
167
- return super().__new__(mcls, name, tuple(bases), dict(namespace))
141
+ self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
168
142
 
169
- bases = list(bases)
170
- namespace = dict(namespace)
143
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
144
+ super().__init_subclass__(**kwargs)
145
+
146
+ ns = cls.__dict__
171
147
 
172
148
  objs = {}
173
- mro = c3.merge([list(b.__mro__) for b in bases])
174
- for bns in [bcls.__dict__ for bcls in reversed(mro)] + [namespace]:
149
+ mro = cls.__mro__[::-1]
150
+ for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
175
151
  bseen = set() # type: ignore
176
152
  for k, v in bns.items():
177
- if isinstance(v, (Command, Arg)):
178
- check.not_in(v, bseen)
153
+ if isinstance(v, (ArgparseCommand, ArgparseArg)):
154
+ check_not_in(v, bseen)
179
155
  bseen.add(v)
180
156
  objs[k] = v
181
157
  elif k in objs:
182
158
  del [k]
183
159
 
184
- anns = ta.get_type_hints(_AnnotationBox({
160
+ anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
185
161
  **{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
186
- **namespace.get('__annotations__', {}),
187
- }), globalns=namespace.get('__globals__', {}))
162
+ **ns.get('__annotations__', {}),
163
+ }), globalns=ns.get('__globals__', {}))
188
164
 
189
- if 'parser' in namespace:
190
- parser = check.isinstance(namespace.pop('parser'), ArgumentParser)
165
+ if '_parser' in ns:
166
+ parser = check_isinstance(ns['_parser'], argparse.ArgumentParser)
191
167
  else:
192
- parser = ArgumentParser()
193
- namespace['_parser'] = parser
168
+ parser = argparse.ArgumentParser()
169
+ setattr(cls, '_parser', parser)
194
170
 
195
171
  subparsers = parser.add_subparsers()
196
172
  for att, obj in objs.items():
197
- if isinstance(obj, Command):
173
+ if isinstance(obj, ArgparseCommand):
198
174
  if obj.parent is not None:
199
175
  raise NotImplementedError
200
176
  for cn in [obj.name, *(obj.aliases or [])]:
@@ -203,7 +179,7 @@ class _CliMeta(type):
203
179
  if (
204
180
  len(arg.args) == 1 and
205
181
  isinstance(arg.args[0], str) and
206
- not (n := check.isinstance(arg.args[0], str)).startswith('-') and
182
+ not (n := check_isinstance(arg.args[0], str)).startswith('-') and
207
183
  'metavar' not in arg.kwargs
208
184
  ):
209
185
  cparser.add_argument(
@@ -215,9 +191,9 @@ class _CliMeta(type):
215
191
  cparser.add_argument(*arg.args, **arg.kwargs)
216
192
  cparser.set_defaults(_cmd=obj)
217
193
 
218
- elif isinstance(obj, Arg):
194
+ elif isinstance(obj, ArgparseArg):
219
195
  if att in anns:
220
- akwargs = get_arg_ann_kwargs(anns[att])
196
+ akwargs = _get_argparse_arg_ann_kwargs(anns[att])
221
197
  obj.kwargs = {**akwargs, **obj.kwargs}
222
198
  if not obj.dest:
223
199
  if 'dest' in obj.kwargs:
@@ -229,22 +205,10 @@ class _CliMeta(type):
229
205
  else:
230
206
  raise TypeError(obj)
231
207
 
232
- return super().__new__(mcls, name, tuple(bases), namespace)
233
-
234
-
235
- class Cli(metaclass=_CliMeta):
236
-
237
- def __init__(self, argv: ta.Sequence[str] | None = None) -> None:
238
- super().__init__()
239
-
240
- self._argv = argv if argv is not None else sys.argv[1:]
241
-
242
- self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
243
-
244
- _parser: ta.ClassVar[ArgumentParser]
208
+ _parser: ta.ClassVar[argparse.ArgumentParser]
245
209
 
246
210
  @classmethod
247
- def get_parser(cls) -> ArgumentParser:
211
+ def get_parser(cls) -> argparse.ArgumentParser:
248
212
  return cls._parser
249
213
 
250
214
  @property
@@ -252,17 +216,17 @@ class Cli(metaclass=_CliMeta):
252
216
  return self._argv
253
217
 
254
218
  @property
255
- def args(self) -> Namespace:
219
+ def args(self) -> argparse.Namespace:
256
220
  return self._args
257
221
 
258
222
  @property
259
223
  def unknown_args(self) -> ta.Sequence[str]:
260
224
  return self._unknown_args
261
225
 
262
- def _run_cmd(self, cmd: Command) -> int | None:
226
+ def _run_cmd(self, cmd: ArgparseCommand) -> ta.Optional[int]:
263
227
  return cmd.__get__(self, type(self))()
264
228
 
265
- def __call__(self) -> int | None:
229
+ def __call__(self) -> ta.Optional[int]:
266
230
  cmd = getattr(self.args, '_cmd', None)
267
231
 
268
232
  if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
@@ -270,7 +234,7 @@ class Cli(metaclass=_CliMeta):
270
234
  if (parser := self.get_parser()).exit_on_error: # type: ignore
271
235
  parser.error(msg)
272
236
  else:
273
- raise ArgumentError(None, msg)
237
+ raise argparse.ArgumentError(None, msg)
274
238
 
275
239
  if cmd is None:
276
240
  self.get_parser().print_help()
omlish/check.py CHANGED
@@ -1,6 +1,8 @@
1
1
  """
2
2
  TODO:
3
3
  - def maybe(v: lang.Maybe[T])
4
+ - patch / override lite.check ?
5
+ - checker interface?
4
6
  """
5
7
  import collections
6
8
  import threading
omlish/io/__init__.py CHANGED
@@ -1,3 +0,0 @@
1
- from ..lite.io import ( # noqa
2
- DelimitingBuffer,
3
- )
@@ -1,11 +1,12 @@
1
1
  # ruff: noqa: UP007
2
+ # @omlish-lite
2
3
  import io
3
4
  import typing as ta
4
5
 
5
- from .check import check_isinstance
6
- from .check import check_non_empty
7
- from .check import check_not_none
8
- from .strings import attr_repr
6
+ from ..lite.check import check_isinstance
7
+ from ..lite.check import check_not_empty
8
+ from ..lite.check import check_not_none
9
+ from ..lite.strings import attr_repr
9
10
 
10
11
 
11
12
  class DelimitingBuffer:
@@ -192,7 +193,7 @@ class IncrementalWriteBuffer:
192
193
  ) -> None:
193
194
  super().__init__()
194
195
 
195
- check_non_empty(data)
196
+ check_not_empty(data)
196
197
  self._len = len(data)
197
198
  self._write_size = write_size
198
199
 
@@ -207,11 +208,11 @@ class IncrementalWriteBuffer:
207
208
  return self._len - self._pos
208
209
 
209
210
  def write(self, fn: ta.Callable[[bytes], int]) -> int:
210
- lst = check_non_empty(self._lst)
211
+ lst = check_not_empty(self._lst)
211
212
 
212
213
  t = 0
213
214
  for i, d in enumerate(lst): # noqa
214
- n = fn(check_non_empty(d))
215
+ n = fn(check_not_empty(d))
215
216
  if not n:
216
217
  break
217
218
  t += n
File without changes
@@ -2,15 +2,15 @@
2
2
  import socket
3
3
  import typing as ta
4
4
 
5
- from ..check import check_isinstance
6
- from ..check import check_none
7
- from ..check import check_not_none
8
- from ..check import check_state
9
- from ..http.coroserver import CoroHttpServer
10
- from ..http.handlers import HttpHandler
11
- from ..io import IncrementalWriteBuffer
12
- from ..io import ReadableListBuffer
13
- from ..socket import SocketAddress
5
+ from ...lite.check import check_isinstance
6
+ from ...lite.check import check_none
7
+ from ...lite.check import check_not_none
8
+ from ...lite.check import check_state
9
+ from ...lite.http.coroserver import CoroHttpServer
10
+ from ...lite.http.handlers import HttpHandler
11
+ from ...lite.socket import SocketAddress
12
+ from ..buffers import IncrementalWriteBuffer
13
+ from ..buffers import ReadableListBuffer
14
14
  from .handlers import SocketFdioHandler
15
15
 
16
16
 
@@ -3,8 +3,8 @@ import abc
3
3
  import socket
4
4
  import typing as ta
5
5
 
6
- from ..check import check_not_none
7
- from ..socket import SocketAddress
6
+ from ...lite.check import check_not_none
7
+ from ...lite.socket import SocketAddress
8
8
 
9
9
 
10
10
  class FdioHandler(abc.ABC):
File without changes
@@ -0,0 +1,63 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import asyncio.base_subprocess
3
+ import asyncio.subprocess
4
+ import typing as ta
5
+
6
+
7
+ AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
8
+
9
+
10
+ ##
11
+
12
+
13
+ ASYNCIO_DEFAULT_BUFFER_LIMIT = 2 ** 16
14
+
15
+
16
+ async def asyncio_open_stream_reader(
17
+ f: ta.IO,
18
+ loop: ta.Any = None,
19
+ *,
20
+ limit: int = ASYNCIO_DEFAULT_BUFFER_LIMIT,
21
+ ) -> asyncio.StreamReader:
22
+ if loop is None:
23
+ loop = asyncio.get_running_loop()
24
+
25
+ reader = asyncio.StreamReader(limit=limit, loop=loop)
26
+ await loop.connect_read_pipe(
27
+ lambda: asyncio.StreamReaderProtocol(reader, loop=loop),
28
+ f,
29
+ )
30
+
31
+ return reader
32
+
33
+
34
+ async def asyncio_open_stream_writer(
35
+ f: ta.IO,
36
+ loop: ta.Any = None,
37
+ ) -> asyncio.StreamWriter:
38
+ if loop is None:
39
+ loop = asyncio.get_running_loop()
40
+
41
+ writer_transport, writer_protocol = await loop.connect_write_pipe(
42
+ lambda: asyncio.streams.FlowControlMixin(loop=loop),
43
+ f,
44
+ )
45
+
46
+ return asyncio.streams.StreamWriter(
47
+ writer_transport,
48
+ writer_protocol,
49
+ None,
50
+ loop,
51
+ )
52
+
53
+
54
+ ##
55
+
56
+
57
+ def asyncio_maybe_timeout(
58
+ fut: AwaitableT,
59
+ timeout: ta.Optional[float] = None,
60
+ ) -> AwaitableT:
61
+ if timeout is not None:
62
+ fut = asyncio.wait_for(fut, timeout) # type: ignore
63
+ return fut
@@ -0,0 +1,297 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import asyncio.base_subprocess
3
+ import asyncio.subprocess
4
+ import contextlib
5
+ import functools
6
+ import logging
7
+ import subprocess
8
+ import sys
9
+ import typing as ta
10
+
11
+ from ..check import check_equal
12
+ from ..check import check_isinstance
13
+ from ..check import check_not_none
14
+ from ..check import check_single
15
+ from ..logs import log
16
+ from ..subprocesses import DEFAULT_SUBPROCESS_TRY_EXCEPTIONS
17
+ from ..subprocesses import prepare_subprocess_invocation
18
+ from ..subprocesses import subprocess_common_context
19
+ from .asyncio import asyncio_maybe_timeout
20
+
21
+
22
+ T = ta.TypeVar('T')
23
+
24
+
25
+ ##
26
+
27
+
28
+ @contextlib.asynccontextmanager
29
+ async def asyncio_subprocess_popen(
30
+ *cmd: str,
31
+ shell: bool = False,
32
+ timeout: ta.Optional[float] = None,
33
+ **kwargs: ta.Any,
34
+ ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
35
+ fac: ta.Any
36
+ if shell:
37
+ fac = functools.partial(
38
+ asyncio.create_subprocess_shell,
39
+ check_single(cmd),
40
+ )
41
+ else:
42
+ fac = functools.partial(
43
+ asyncio.create_subprocess_exec,
44
+ *cmd,
45
+ )
46
+
47
+ with subprocess_common_context(
48
+ *cmd,
49
+ shell=shell,
50
+ timeout=timeout,
51
+ **kwargs,
52
+ ):
53
+ proc: asyncio.subprocess.Process
54
+ proc = await fac(**kwargs)
55
+ try:
56
+ yield proc
57
+
58
+ finally:
59
+ await asyncio_maybe_timeout(proc.wait(), timeout)
60
+
61
+
62
+ ##
63
+
64
+
65
+ class AsyncioProcessCommunicator:
66
+ def __init__(
67
+ self,
68
+ proc: asyncio.subprocess.Process,
69
+ loop: ta.Optional[ta.Any] = None,
70
+ ) -> None:
71
+ super().__init__()
72
+
73
+ if loop is None:
74
+ loop = asyncio.get_running_loop()
75
+
76
+ self._proc = proc
77
+ self._loop = loop
78
+
79
+ self._transport: asyncio.base_subprocess.BaseSubprocessTransport = check_isinstance(
80
+ proc._transport, # type: ignore # noqa
81
+ asyncio.base_subprocess.BaseSubprocessTransport,
82
+ )
83
+
84
+ @property
85
+ def _debug(self) -> bool:
86
+ return self._loop.get_debug()
87
+
88
+ async def _feed_stdin(self, input: bytes) -> None: # noqa
89
+ stdin = check_not_none(self._proc.stdin)
90
+ try:
91
+ if input is not None:
92
+ stdin.write(input)
93
+ if self._debug:
94
+ log.debug('%r communicate: feed stdin (%s bytes)', self, len(input))
95
+
96
+ await stdin.drain()
97
+
98
+ except (BrokenPipeError, ConnectionResetError) as exc:
99
+ # communicate() ignores BrokenPipeError and ConnectionResetError. write() and drain() can raise these
100
+ # exceptions.
101
+ if self._debug:
102
+ log.debug('%r communicate: stdin got %r', self, exc)
103
+
104
+ if self._debug:
105
+ log.debug('%r communicate: close stdin', self)
106
+
107
+ stdin.close()
108
+
109
+ async def _noop(self) -> None:
110
+ return None
111
+
112
+ async def _read_stream(self, fd: int) -> bytes:
113
+ transport: ta.Any = check_not_none(self._transport.get_pipe_transport(fd))
114
+
115
+ if fd == 2:
116
+ stream = check_not_none(self._proc.stderr)
117
+ else:
118
+ check_equal(fd, 1)
119
+ stream = check_not_none(self._proc.stdout)
120
+
121
+ if self._debug:
122
+ name = 'stdout' if fd == 1 else 'stderr'
123
+ log.debug('%r communicate: read %s', self, name)
124
+
125
+ output = await stream.read()
126
+
127
+ if self._debug:
128
+ name = 'stdout' if fd == 1 else 'stderr'
129
+ log.debug('%r communicate: close %s', self, name)
130
+
131
+ transport.close()
132
+
133
+ return output
134
+
135
+ class Communication(ta.NamedTuple):
136
+ stdout: ta.Optional[bytes]
137
+ stderr: ta.Optional[bytes]
138
+
139
+ async def _communicate(
140
+ self,
141
+ input: ta.Any = None, # noqa
142
+ ) -> Communication:
143
+ stdin_fut: ta.Any
144
+ if self._proc.stdin is not None:
145
+ stdin_fut = self._feed_stdin(input)
146
+ else:
147
+ stdin_fut = self._noop()
148
+
149
+ stdout_fut: ta.Any
150
+ if self._proc.stdout is not None:
151
+ stdout_fut = self._read_stream(1)
152
+ else:
153
+ stdout_fut = self._noop()
154
+
155
+ stderr_fut: ta.Any
156
+ if self._proc.stderr is not None:
157
+ stderr_fut = self._read_stream(2)
158
+ else:
159
+ stderr_fut = self._noop()
160
+
161
+ stdin_res, stdout_res, stderr_res = await asyncio.gather(stdin_fut, stdout_fut, stderr_fut)
162
+
163
+ await self._proc.wait()
164
+
165
+ return AsyncioProcessCommunicator.Communication(stdout_res, stderr_res)
166
+
167
+ async def communicate(
168
+ self,
169
+ input: ta.Any = None, # noqa
170
+ timeout: ta.Optional[float] = None,
171
+ ) -> Communication:
172
+ return await asyncio_maybe_timeout(self._communicate(input), timeout)
173
+
174
+
175
+ async def asyncio_subprocess_communicate(
176
+ proc: asyncio.subprocess.Process,
177
+ input: ta.Any = None, # noqa
178
+ timeout: ta.Optional[float] = None,
179
+ ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
180
+ return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
181
+
182
+
183
+ ##
184
+
185
+
186
+ async def _asyncio_subprocess_check_run(
187
+ *args: str,
188
+ input: ta.Any = None, # noqa
189
+ timeout: ta.Optional[float] = None,
190
+ **kwargs: ta.Any,
191
+ ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
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 proc.returncode:
199
+ raise subprocess.CalledProcessError(
200
+ proc.returncode,
201
+ args,
202
+ output=stdout,
203
+ stderr=stderr,
204
+ )
205
+
206
+ return stdout, stderr
207
+
208
+
209
+ async def asyncio_subprocess_check_call(
210
+ *args: str,
211
+ stdout: ta.Any = sys.stderr,
212
+ input: ta.Any = None, # noqa
213
+ timeout: ta.Optional[float] = None,
214
+ **kwargs: ta.Any,
215
+ ) -> None:
216
+ _, _ = await _asyncio_subprocess_check_run(
217
+ *args,
218
+ stdout=stdout,
219
+ input=input,
220
+ timeout=timeout,
221
+ **kwargs,
222
+ )
223
+
224
+
225
+ async def asyncio_subprocess_check_output(
226
+ *args: str,
227
+ input: ta.Any = None, # noqa
228
+ timeout: ta.Optional[float] = None,
229
+ **kwargs: ta.Any,
230
+ ) -> bytes:
231
+ stdout, stderr = await _asyncio_subprocess_check_run(
232
+ *args,
233
+ stdout=asyncio.subprocess.PIPE,
234
+ input=input,
235
+ timeout=timeout,
236
+ **kwargs,
237
+ )
238
+
239
+ return check_not_none(stdout)
240
+
241
+
242
+ async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
243
+ return (await asyncio_subprocess_check_output(*args, **kwargs)).decode().strip()
244
+
245
+
246
+ ##
247
+
248
+
249
+ async def _asyncio_subprocess_try_run(
250
+ fn: ta.Callable[..., ta.Awaitable[T]],
251
+ *args: ta.Any,
252
+ try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
253
+ **kwargs: ta.Any,
254
+ ) -> ta.Union[T, Exception]:
255
+ try:
256
+ return await fn(*args, **kwargs)
257
+ except try_exceptions as e: # noqa
258
+ if log.isEnabledFor(logging.DEBUG):
259
+ log.exception('command failed')
260
+ return e
261
+
262
+
263
+ async def asyncio_subprocess_try_call(
264
+ *args: str,
265
+ try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
266
+ **kwargs: ta.Any,
267
+ ) -> bool:
268
+ if isinstance(await _asyncio_subprocess_try_run(
269
+ asyncio_subprocess_check_call,
270
+ *args,
271
+ try_exceptions=try_exceptions,
272
+ **kwargs,
273
+ ), Exception):
274
+ return False
275
+ else:
276
+ return True
277
+
278
+
279
+ async def asyncio_subprocess_try_output(
280
+ *args: str,
281
+ try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
282
+ **kwargs: ta.Any,
283
+ ) -> ta.Optional[bytes]:
284
+ if isinstance(ret := await _asyncio_subprocess_try_run(
285
+ asyncio_subprocess_check_output,
286
+ *args,
287
+ try_exceptions=try_exceptions,
288
+ **kwargs,
289
+ ), Exception):
290
+ return None
291
+ else:
292
+ return ret
293
+
294
+
295
+ async def asyncio_subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
296
+ out = await asyncio_subprocess_try_output(*args, **kwargs)
297
+ return out.decode().strip() if out is not None else None
omlish/lite/cached.py CHANGED
@@ -7,7 +7,10 @@ T = ta.TypeVar('T')
7
7
  CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
8
8
 
9
9
 
10
- class _cached_nullary: # noqa
10
+ ##
11
+
12
+
13
+ class _AbstractCachedNullary:
11
14
  def __init__(self, fn):
12
15
  super().__init__()
13
16
  self._fn = fn
@@ -15,20 +18,42 @@ class _cached_nullary: # noqa
15
18
  functools.update_wrapper(self, fn)
16
19
 
17
20
  def __call__(self, *args, **kwargs): # noqa
18
- if self._value is self._missing:
19
- self._value = self._fn()
20
- return self._value
21
+ raise TypeError
21
22
 
22
23
  def __get__(self, instance, owner): # noqa
23
24
  bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
24
25
  return bound
25
26
 
26
27
 
28
+ ##
29
+
30
+
31
+ class _CachedNullary(_AbstractCachedNullary):
32
+ def __call__(self, *args, **kwargs): # noqa
33
+ if self._value is self._missing:
34
+ self._value = self._fn()
35
+ return self._value
36
+
37
+
27
38
  def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
28
- return _cached_nullary(fn)
39
+ return _CachedNullary(fn)
29
40
 
30
41
 
31
42
  def static_init(fn: CallableT) -> CallableT:
32
43
  fn = cached_nullary(fn)
33
44
  fn()
34
45
  return fn
46
+
47
+
48
+ ##
49
+
50
+
51
+ class _AsyncCachedNullary(_AbstractCachedNullary):
52
+ async def __call__(self, *args, **kwargs):
53
+ if self._value is self._missing:
54
+ self._value = await self._fn()
55
+ return self._value
56
+
57
+
58
+ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
59
+ return _AsyncCachedNullary(fn)
omlish/lite/check.py CHANGED
@@ -41,6 +41,11 @@ def check_non_empty_str(v: ta.Optional[str]) -> str:
41
41
  return v
42
42
 
43
43
 
44
+ def check_arg(v: bool, msg: str = 'Illegal argument') -> None:
45
+ if not v:
46
+ raise ValueError(msg)
47
+
48
+
44
49
  def check_state(v: bool, msg: str = 'Illegal state') -> None:
45
50
  if not v:
46
51
  raise ValueError(msg)
@@ -93,7 +98,7 @@ def check_empty(v: SizedT) -> SizedT:
93
98
  return v
94
99
 
95
100
 
96
- def check_non_empty(v: SizedT) -> SizedT:
101
+ def check_not_empty(v: SizedT) -> SizedT:
97
102
  if not len(v):
98
103
  raise ValueError(v)
99
104
  return v
@@ -0,0 +1,22 @@
1
+ import ctypes as ct
2
+ import sys
3
+
4
+
5
+ LINUX_PR_SET_PDEATHSIG = 1 # Second arg is a signal
6
+ LINUX_PR_GET_PDEATHSIG = 2 # Second arg is a ptr to return the signal
7
+
8
+
9
+ def set_process_deathsig(sig: int) -> bool:
10
+ if sys.platform == 'linux':
11
+ libc = ct.CDLL('libc.so.6')
12
+
13
+ # int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
14
+ libc.prctl.restype = ct.c_int
15
+ libc.prctl.argtypes = [ct.c_int, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong]
16
+
17
+ libc.prctl(LINUX_PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
18
+
19
+ return True
20
+
21
+ else:
22
+ return False
@@ -1,15 +1,18 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ import contextlib
2
3
  import logging
3
4
  import os
4
5
  import shlex
5
6
  import subprocess
6
7
  import sys
8
+ import time
7
9
  import typing as ta
8
10
 
9
11
  from .logs import log
10
12
  from .runtime import is_debugger_attached
11
13
 
12
14
 
15
+ T = ta.TypeVar('T')
13
16
  SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
14
17
 
15
18
 
@@ -40,7 +43,7 @@ def subprocess_maybe_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
40
43
  return args
41
44
 
42
45
 
43
- def _prepare_subprocess_invocation(
46
+ def prepare_subprocess_invocation(
44
47
  *args: str,
45
48
  env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
46
49
  extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
@@ -48,9 +51,9 @@ def _prepare_subprocess_invocation(
48
51
  shell: bool = False,
49
52
  **kwargs: ta.Any,
50
53
  ) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
51
- log.debug(args)
54
+ log.debug('prepare_subprocess_invocation: args=%r', args)
52
55
  if extra_env:
53
- log.debug(extra_env)
56
+ log.debug('prepare_subprocess_invocation: extra_env=%r', extra_env)
54
57
 
55
58
  if extra_env:
56
59
  env = {**(env if env is not None else os.environ), **extra_env}
@@ -69,14 +72,46 @@ def _prepare_subprocess_invocation(
69
72
  )
70
73
 
71
74
 
72
- def subprocess_check_call(*args: str, stdout=sys.stderr, **kwargs: ta.Any) -> None:
73
- args, kwargs = _prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
74
- return subprocess.check_call(args, **kwargs) # type: ignore
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)
93
+
94
+
95
+ ##
96
+
97
+
98
+ def subprocess_check_call(
99
+ *args: str,
100
+ stdout: ta.Any = sys.stderr,
101
+ **kwargs: ta.Any,
102
+ ) -> 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
75
106
 
76
107
 
77
- def subprocess_check_output(*args: str, **kwargs: ta.Any) -> bytes:
78
- args, kwargs = _prepare_subprocess_invocation(*args, **kwargs)
79
- return subprocess.check_output(args, **kwargs)
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)
80
115
 
81
116
 
82
117
  def subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
@@ -92,16 +127,31 @@ DEFAULT_SUBPROCESS_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
92
127
  )
93
128
 
94
129
 
95
- def subprocess_try_call(
96
- *args: str,
130
+ def _subprocess_try_run(
131
+ fn: ta.Callable[..., T],
132
+ *args: ta.Any,
97
133
  try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
98
134
  **kwargs: ta.Any,
99
- ) -> bool:
135
+ ) -> ta.Union[T, Exception]:
100
136
  try:
101
- subprocess_check_call(*args, **kwargs)
137
+ return fn(*args, **kwargs)
102
138
  except try_exceptions as e: # noqa
103
139
  if log.isEnabledFor(logging.DEBUG):
104
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,
153
+ **kwargs,
154
+ ), Exception):
105
155
  return False
106
156
  else:
107
157
  return True
@@ -112,12 +162,15 @@ def subprocess_try_output(
112
162
  try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
113
163
  **kwargs: ta.Any,
114
164
  ) -> ta.Optional[bytes]:
115
- try:
116
- return subprocess_check_output(*args, **kwargs)
117
- except try_exceptions as e: # noqa
118
- if log.isEnabledFor(logging.DEBUG):
119
- log.exception('command failed')
165
+ if isinstance(ret := _subprocess_try_run(
166
+ subprocess_check_output,
167
+ *args,
168
+ try_exceptions=try_exceptions,
169
+ **kwargs,
170
+ ), Exception):
120
171
  return None
172
+ else:
173
+ return ret
121
174
 
122
175
 
123
176
  def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
@@ -142,7 +142,12 @@ class AsyncsPlugin:
142
142
  if len(bes) > 1 and set(bes) != {'trio', 'trio_asyncio'}:
143
143
  raise Exception(f'{item.nodeid}: multiple async backends specified: {bes}')
144
144
  elif is_async_function(item.obj) and not bes:
145
- raise Exception(f'{item.nodeid}: async def function and no async plugin specified')
145
+ from _pytest.unittest import UnitTestCase # noqa
146
+ if isinstance(item.parent, UnitTestCase):
147
+ # unittest handles these itself.
148
+ pass
149
+ else:
150
+ raise Exception(f'{item.nodeid}: async def function and no async plugin specified')
146
151
 
147
152
  if 'trio_asyncio' in bes:
148
153
  obj = item.obj
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev147
3
+ Version: 0.0.0.dev149
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,10 +1,9 @@
1
1
  omlish/.manifests.json,sha256=RX24SRc6DCEg77PUVnaXOKCWa5TF_c9RQJdGIf7gl9c,1135
2
- omlish/__about__.py,sha256=cjS87eUIaMvfyFbRNKoCGNxC70RIFRVnspSCKl5DPLA,3409
2
+ omlish/__about__.py,sha256=l0_vBxdzereK5v8M0TtiX5ahNihp0_HGdpH8Oq5x5Ag,3409
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
- omlish/argparse.py,sha256=cqKGAqcxuxv_s62z0gq29L9KAvg_3-_rFvXKjVpRJjo,8126
5
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
6
5
  omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
7
- omlish/check.py,sha256=CBOCfl6ANZ7CKke2bGfQfUew9m22_ke0GvfEDO4Sjug,10595
6
+ omlish/check.py,sha256=KWS5IRrBCjzfK_nTRQeF0sRlbo46G-6w4dvkeJDUw8I,10651
8
7
  omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
9
8
  omlish/defs.py,sha256=9uUjJuVIbCBL3g14fyzAp-9gH935MFofvlfOGwcBIaM,4913
10
9
  omlish/dynamic.py,sha256=35C_cCX_Vq2HrHzGk5T-zbrMvmUdiIiwDzDNixczoDo,6541
@@ -75,6 +74,9 @@ omlish/antlr/_runtime/tree/__init__.py,sha256=Jn5lqTVbeUQXD5a4IxDHKibOatAQWVTlaQ
75
74
  omlish/antlr/_runtime/xpath/XPath.py,sha256=CbS0Fpnd2aRt_nQUBJlTpoHpxCyT9qbVW8ldj1aQJKY,9643
76
75
  omlish/antlr/_runtime/xpath/XPathLexer.py,sha256=xFtdr4ZXMZxb2dnB_ggWyhvlQiC7RXQlDS5ePhTyOGg,3505
77
76
  omlish/antlr/_runtime/xpath/__init__.py,sha256=lMd_BbXYdlDhZQN_q0TKN978XW5G0pq618F0NaLkpFE,71
77
+ omlish/argparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ omlish/argparse/all.py,sha256=EfUSf27vFWqa4Q93AycU5YRsrHt-Nx3pU3uNVapb-EE,1054
79
+ omlish/argparse/cli.py,sha256=SULYDE7BDGcU_dfKLguIcBQ0k_ttsyCjPjeqdHBN3Qc,7607
78
80
  omlish/asyncs/__init__.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
79
81
  omlish/asyncs/anyio.py,sha256=gfpx-D8QGmUfhnQxHEaHXcAP8zSMQjcGw4COFTGNnHI,8021
80
82
  omlish/asyncs/asyncio.py,sha256=JfM59QgB3asgEbrps0zoVbNjWD4kL2XdsEkRMEIoFos,971
@@ -256,8 +258,9 @@ omlish/inject/impl/privates.py,sha256=alpCYyk5VJ9lJknbRH2nLVNFYVvFhkj-VC1Vco3zCF
256
258
  omlish/inject/impl/providers.py,sha256=QnwhsujJFIHC0JTgd2Wlo1kP53i3CWTrj1nKU2DNxwg,2375
257
259
  omlish/inject/impl/proxy.py,sha256=1ko0VaKqzu9UG8bIldp9xtUrAVUOFTKWKTjOCqIGr4s,1636
258
260
  omlish/inject/impl/scopes.py,sha256=hKnzNieB-fJSFEXDP_QG1mCfIKoVFIfFlf9LiIt5tk4,5920
259
- omlish/io/__init__.py,sha256=aaIEsXTSfytW-oEkUWczdUJ_ifFY7ihIpyidIbfjkwY,56
261
+ omlish/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
260
262
  omlish/io/abc.py,sha256=Cxs8KB1B_69rxpUYxI-MTsilAmNooJJn3w07DKqYKkE,1255
263
+ omlish/io/buffers.py,sha256=JoifktnJxnNfL8WdhqbcDwEzBcOm-3jJbFk6CooE6aQ,5378
261
264
  omlish/io/pyio.py,sha256=q4RBFVpBE5PYjnGPGT-_4pcZb7dFJmLJ4LtI8OoDRQY,95433
262
265
  omlish/io/trampoline.py,sha256=oUKTQg1F5xQS1431Kt7MbK-NZpX509ubcXU-s86xJr8,7171
263
266
  omlish/io/compress/__init__.py,sha256=qV-aDfPWykTMYcoQmE8THZ4KFDRzqwN3QPPNEJVarXY,86
@@ -272,6 +275,12 @@ omlish/io/compress/lzma.py,sha256=8qxi7TniLN00LyJIJLyp6W7UUU50JBaPxxoXYg2j2XQ,22
272
275
  omlish/io/compress/snappy.py,sha256=kCPgZ7PTBAxAnmYzpQCq4HKUIJ4APeAEXsU3Vg2CaDU,411
273
276
  omlish/io/compress/zlib.py,sha256=MtnVGfzDlRU1LPl2J8Sa3wwgqnTVBx2uclZygWpH9xI,2115
274
277
  omlish/io/compress/zstd.py,sha256=LrYWVHzk-TqWJA_Bnci2i8QOtrqnFFpppLQhLqanDWM,668
278
+ omlish/io/fdio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
+ omlish/io/fdio/corohttp.py,sha256=fJKNpED19W_goXdZ6CN_supOyeaMQNgps-E0TQxxjlo,4132
280
+ omlish/io/fdio/handlers.py,sha256=ZtJ6MOVVRjLuKim9sfGMJsVCRvOTMvFYNPfzp1Cyn7M,1353
281
+ omlish/io/fdio/kqueue.py,sha256=YgGBQibkAUYODYDiGl7Enjtx1oQsJXuDsBLBXgqlLQw,3832
282
+ omlish/io/fdio/manager.py,sha256=q4wWf7nKrNtjx6yPEvrVnFt4UtK_BTvVlquEGw7poEo,1250
283
+ omlish/io/fdio/pollers.py,sha256=yNadAt3W5wd90PFmd3vD77bq5QwoVb2A6SM2JjZpKRs,5507
275
284
  omlish/io/generators/__init__.py,sha256=40DTZUqyss40dlgm68yKAtiAlqeIlYylTi8zaFrUW40,1135
276
285
  omlish/io/generators/consts.py,sha256=4r6IMLBMic6MJHVn9UiORIkkPAuxsqtzFT3KV0fatC0,33
277
286
  omlish/io/generators/direct.py,sha256=A9VJB1rNKU3l-NatpYIwyCLI3R_ybGglmdx6sAtoTo4,324
@@ -311,12 +320,12 @@ omlish/lifecycles/manager.py,sha256=Au66KaO-fI-SEJALaPUJsCHYW2GE20xextk1wKn2BEU,
311
320
  omlish/lifecycles/states.py,sha256=zqMOU2ZU-MDNnWuwauM3_anIAiXM8LoBDElDEraptFg,1292
312
321
  omlish/lifecycles/transitions.py,sha256=qQtFby-h4VzbvgaUqT2NnbNumlcOx9FVVADP9t83xj4,1939
313
322
  omlish/lite/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
314
- omlish/lite/cached.py,sha256=rrc_JEv3sKJIEmCBB6g7DwPvkb1hNFmhg0mxkvuXDJw,848
315
- omlish/lite/check.py,sha256=pQC412ffe_Zh7eHa4C1UYn6fA71Ls1vpVM0ZIOroPAY,1765
323
+ omlish/lite/cached.py,sha256=hBW77-F7ZLtFqbLwVrlqaJ4-iFHMQleMWZXaZN1IubA,1308
324
+ omlish/lite/check.py,sha256=VOf_skD5hRzny_aqxykihQRs0PYd3SwkuihXJ2Bfaps,1874
316
325
  omlish/lite/contextmanagers.py,sha256=DRarS2gx15tbse1YzyI8ZLdBmWYjFgmKPe-i4CSNDYg,1458
326
+ omlish/lite/deathsig.py,sha256=Etz04WX6R2PXQ-BgqJyVJ0C5Pqym6Ds6PAG7p1cqr5o,626
317
327
  omlish/lite/docker.py,sha256=Dj_7lQjs2sFPc_SmUn5CpJF3LnQQnckEBYGBKz8u5tE,392
318
328
  omlish/lite/inject.py,sha256=aRRmFb6azTKF208ogYwVCEopNZx7496Ta1GZmL_IKBA,23716
319
- omlish/lite/io.py,sha256=3ECgUXdRnXyS6pGTSoVr6oB4moI38EpWxTq08zaTM-U,5339
320
329
  omlish/lite/journald.py,sha256=f5Y2Q6-6O3iK_7MoGiwZwoQEOcP7LfkxxQNUR9tMjJM,3882
321
330
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
322
331
  omlish/lite/logs.py,sha256=1pcGu0ekhVCcLUckLSP16VccnAoprjtl5Vkdfm7y1Wg,6184
@@ -331,14 +340,11 @@ omlish/lite/secrets.py,sha256=3Mz3V2jf__XU9qNHcH56sBSw95L3U2UPL24bjvobG0c,816
331
340
  omlish/lite/socket.py,sha256=7OYgkXTcQv0wq7TQuLnl9y6dJA1ZT6Vbc1JH59QlxgY,1792
332
341
  omlish/lite/socketserver.py,sha256=Esy9dAo9dPnNavNx5hW52YZi5hv504a8XQUudMrPs2A,1595
333
342
  omlish/lite/strings.py,sha256=QURcE4-1pKVW8eT_5VCJpXaHDWR2dW2pYOChTJnZDiQ,1504
334
- omlish/lite/subprocesses.py,sha256=1we1S-YQ9kbH36hPWLoh6zKZUARRnq2__ewtX_dVdWU,3633
343
+ omlish/lite/subprocesses.py,sha256=RTs8HJ1Lz8YOZTHw12Ja8KW7Eq4oyDFJZDiG0PSUBKY,4918
335
344
  omlish/lite/typing.py,sha256=U3-JaEnkDSYxK4tsu_MzUn3RP6qALBe5FXQXpD-licE,1090
336
- omlish/lite/fdio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
337
- omlish/lite/fdio/corohttp.py,sha256=FHdakDTGI2UYbCihahuwleyailclxQMUGhpkz3suww4,4080
338
- omlish/lite/fdio/handlers.py,sha256=Wr0O2cvIC8NuLs3yoDHj9ZG4n1g_oVEeT87B0WDsg0Y,1341
339
- omlish/lite/fdio/kqueue.py,sha256=YgGBQibkAUYODYDiGl7Enjtx1oQsJXuDsBLBXgqlLQw,3832
340
- omlish/lite/fdio/manager.py,sha256=q4wWf7nKrNtjx6yPEvrVnFt4UtK_BTvVlquEGw7poEo,1250
341
- omlish/lite/fdio/pollers.py,sha256=yNadAt3W5wd90PFmd3vD77bq5QwoVb2A6SM2JjZpKRs,5507
345
+ omlish/lite/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
346
+ omlish/lite/asyncio/asyncio.py,sha256=tsqQSLl5rG4GZHPYOBqI7V2yuw45ZVuGZEHe-J4QEhE,1320
347
+ omlish/lite/asyncio/subprocesses.py,sha256=crG4FlkVIA19sO2QtIagOCtXk8ak9rLaHgbTjGDy3mk,8274
342
348
  omlish/lite/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
343
349
  omlish/lite/http/coroserver.py,sha256=aBaYjP80yQHQxPxwi7PTYHub-fdRDKsMnB-tM8lBc2o,18095
344
350
  omlish/lite/http/handlers.py,sha256=Yu0P3nqz-frklwCM2PbiWvoJNE-NqeTFLBvpNpqcdtA,753
@@ -487,7 +493,7 @@ omlish/testing/pytest/inject/__init__.py,sha256=pdRKv1HcDmJ_yArKJbYITPXXZthRSGgB
487
493
  omlish/testing/pytest/inject/harness.py,sha256=v4DaKJ0KL8oQjzIMK43Gh8GHP4hiI6-lY37O9lyOHRk,5724
488
494
  omlish/testing/pytest/plugins/__init__.py,sha256=ys1zXrYrNm7Uo6YOIVJ6Bd3dQo6kv387k7MbTYlqZSI,467
489
495
  omlish/testing/pytest/plugins/_registry.py,sha256=IK04KlBgiOJxKAyCCgjpX2R-9tE-btalYJkgjLc8Te8,77
490
- omlish/testing/pytest/plugins/asyncs.py,sha256=SV6oKCy50CGkzLGYX-CT4MfWNqsrH8ONEbIWC3tFcHA,5324
496
+ omlish/testing/pytest/plugins/asyncs.py,sha256=CG-cWWxCtxVIyKJKEjxfFV0MVwYBHPo1mb-umCGz9X8,5532
491
497
  omlish/testing/pytest/plugins/depskip.py,sha256=xithY-OMtjwhv8mcRNkv-WI_PSQtHldQ8H1s60MIXkk,2673
492
498
  omlish/testing/pytest/plugins/logging.py,sha256=1zs6Xe54wiaSjabCviaFXwKkoN97CKm3mA5mEoUeJGs,380
493
499
  omlish/testing/pytest/plugins/managermarks.py,sha256=AP3ty-QB-8O5DkulwUOudBlUOvXMHhBfNyY-0yCmejk,1520
@@ -504,9 +510,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
504
510
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
505
511
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
506
512
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
507
- omlish-0.0.0.dev147.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
508
- omlish-0.0.0.dev147.dist-info/METADATA,sha256=nS6HmxhGs_7YPKXtA-OutWPomt4JOFvPmLAFwYYT7nk,4264
509
- omlish-0.0.0.dev147.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
510
- omlish-0.0.0.dev147.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
511
- omlish-0.0.0.dev147.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
512
- omlish-0.0.0.dev147.dist-info/RECORD,,
513
+ omlish-0.0.0.dev149.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
514
+ omlish-0.0.0.dev149.dist-info/METADATA,sha256=z0AUwvKglpElbQTqOlIdPh5J21U5sNjvUuYimEhJncE,4264
515
+ omlish-0.0.0.dev149.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
516
+ omlish-0.0.0.dev149.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
517
+ omlish-0.0.0.dev149.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
518
+ omlish-0.0.0.dev149.dist-info/RECORD,,
File without changes
File without changes
File without changes
File without changes