omlish 0.0.0.dev141__py3-none-any.whl → 0.0.0.dev142__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,53 @@
1
+ from .consts import ( # noqa
2
+ DEFAULT_BUFFER_SIZE,
3
+ )
4
+
5
+ from .direct import ( # noqa
6
+ DirectGenerator,
7
+
8
+ BytesDirectGenerator,
9
+ StrDirectGenerator,
10
+ )
11
+
12
+ from .readers import ( # noqa
13
+ ReaderGenerator,
14
+ BytesReaderGenerator,
15
+ StrReaderGenerator,
16
+
17
+ ExactReaderGenerator,
18
+ BytesExactReaderGenerator,
19
+ StrExactReaderGenerator,
20
+
21
+ GeneratorReader,
22
+
23
+ PrependableGeneratorReader,
24
+ PrependableBytesGeneratorReader,
25
+ PrependableStrGeneratorReader,
26
+ prependable_bytes_generator_reader,
27
+ prependable_str_generator_reader,
28
+
29
+ BufferedGeneratorReader,
30
+ BufferedBytesGeneratorReader,
31
+ BufferedStrGeneratorReader,
32
+ buffered_bytes_generator_reader,
33
+ buffered_str_generator_reader,
34
+ )
35
+
36
+ from .stepped import ( # noqa
37
+ SteppedGenerator,
38
+ BytesSteppedGenerator,
39
+ StrSteppedGenerator,
40
+ BytesToStrSteppedGenerator,
41
+ StrToBytesSteppedGenerator,
42
+
43
+ SteppedReaderGenerator,
44
+ BytesSteppedReaderGenerator,
45
+ StrSteppedReaderGenerator,
46
+
47
+ flatmap_stepped_generator,
48
+
49
+ joined_bytes_stepped_generator,
50
+ joined_str_stepped_generator,
51
+
52
+ read_into_bytes_stepped_generator,
53
+ )
@@ -0,0 +1 @@
1
+ DEFAULT_BUFFER_SIZE = 4 * 0x1000
@@ -0,0 +1,13 @@
1
+ import typing as ta
2
+
3
+
4
+ O = ta.TypeVar('O')
5
+ I = ta.TypeVar('I')
6
+ R = ta.TypeVar('R')
7
+
8
+
9
+ # Direct generators yield outputs 1:1 with inputs.
10
+ DirectGenerator: ta.TypeAlias = ta.Generator[O, I, R]
11
+
12
+ BytesDirectGenerator: ta.TypeAlias = DirectGenerator[bytes, bytes, R]
13
+ StrDirectGenerator: ta.TypeAlias = DirectGenerator[str, str, R]
@@ -8,21 +8,29 @@ import abc
8
8
  import typing as ta
9
9
 
10
10
  from ... import check
11
+ from .consts import DEFAULT_BUFFER_SIZE
11
12
 
12
13
 
13
14
  T = ta.TypeVar('T')
15
+
16
+ O = ta.TypeVar('O')
14
17
  I = ta.TypeVar('I')
15
18
  R = ta.TypeVar('R')
19
+
16
20
  AnyT = ta.TypeVar('AnyT', bound=ta.Any)
17
21
 
18
22
 
23
+ # Reader generators yield ints of amounts of data needed, or None for needing any amount of data.
19
24
  ReaderGenerator: ta.TypeAlias = ta.Generator[int | None, I, R]
20
- ExactReaderGenerator: ta.TypeAlias = ta.Generator[int, I, R]
21
25
 
22
26
  BytesReaderGenerator: ta.TypeAlias = ReaderGenerator[bytes, R]
23
- BytesExactReaderGenerator: ta.TypeAlias = ExactReaderGenerator[bytes, R]
24
-
25
27
  StrReaderGenerator: ta.TypeAlias = ReaderGenerator[str, R]
28
+
29
+
30
+ # Exact reader generators always specify read sizes.
31
+ ExactReaderGenerator: ta.TypeAlias = ta.Generator[int, I, R]
32
+
33
+ BytesExactReaderGenerator: ta.TypeAlias = ExactReaderGenerator[bytes, R]
26
34
  StrExactReaderGenerator: ta.TypeAlias = ExactReaderGenerator[str, R]
27
35
 
28
36
 
@@ -44,10 +52,10 @@ class _StrJoiner:
44
52
 
45
53
  class GeneratorReader(abc.ABC, ta.Generic[T]):
46
54
  @abc.abstractmethod
47
- def read(self, sz: int | None) -> ta.Generator[int | None, T, T]:
55
+ def read(self, sz: int | None) -> ReaderGenerator[T, T]:
48
56
  raise NotImplementedError
49
57
 
50
- def read_exact(self, sz: int) -> ta.Generator[int | None, T, T]:
58
+ def read_exact(self, sz: int) -> ReaderGenerator[T, T]:
51
59
  d: ta.Any = yield from self.read(sz)
52
60
  if len(d) != sz:
53
61
  raise EOFError(f'GeneratorReader got {len(d)}, expected {sz}')
@@ -67,7 +75,7 @@ class PrependableGeneratorReader(GeneratorReader[AnyT]):
67
75
  def _join(self, lst: list[AnyT]) -> AnyT:
68
76
  raise NotImplementedError
69
77
 
70
- def read(self, sz: int | None) -> ta.Generator[int | None, AnyT, AnyT]:
78
+ def read(self, sz: int | None) -> ReaderGenerator[AnyT, AnyT]:
71
79
  if not self._queue:
72
80
  d: AnyT = check.not_none((yield sz))
73
81
  return d
@@ -126,8 +134,6 @@ prependable_str_generator_reader = PrependableStrGeneratorReader
126
134
 
127
135
 
128
136
  class BufferedGeneratorReader(PrependableGeneratorReader[AnyT], abc.ABC):
129
- DEFAULT_BUFFER_SIZE = 4 * 0x1000
130
-
131
137
  def __init__(
132
138
  self,
133
139
  buffer_size: int = DEFAULT_BUFFER_SIZE,
@@ -138,7 +144,7 @@ class BufferedGeneratorReader(PrependableGeneratorReader[AnyT], abc.ABC):
138
144
 
139
145
  self._buffer_size = buffer_size
140
146
 
141
- def read(self, sz: int | None) -> ta.Generator[int | None, AnyT, AnyT]:
147
+ def read(self, sz: int | None) -> ReaderGenerator[AnyT, AnyT]:
142
148
  g = super().read(sz)
143
149
  i: ta.Any = None
144
150
  while True:
@@ -1,18 +1,40 @@
1
1
  import typing as ta
2
2
 
3
+ from ... import check
3
4
  from ... import lang
5
+ from .consts import DEFAULT_BUFFER_SIZE
6
+ from .direct import BytesDirectGenerator
7
+ from .direct import StrDirectGenerator
4
8
 
5
9
 
6
10
  T = ta.TypeVar('T')
7
- I = ta.TypeVar('I')
11
+
8
12
  O = ta.TypeVar('O')
13
+ I = ta.TypeVar('I')
14
+ R = ta.TypeVar('R')
15
+
9
16
  OF = ta.TypeVar('OF')
10
17
  OT = ta.TypeVar('OT')
11
- R = ta.TypeVar('R')
12
18
 
13
19
 
20
+ # Stepped generators accept a non-None input, then in response yield zero or more non-None outputs, until yielding None
21
+ # to signal they need more input again.
14
22
  SteppedGenerator: ta.TypeAlias = ta.Generator[O | None, I | None, R]
15
23
 
24
+ # Conventionally, these are sent and themselves yield an empty value to signify termination.
25
+ BytesSteppedGenerator: ta.TypeAlias = SteppedGenerator[bytes, bytes, R]
26
+ StrSteppedGenerator: ta.TypeAlias = SteppedGenerator[str, str, R]
27
+
28
+ BytesToStrSteppedGenerator: ta.TypeAlias = SteppedGenerator[str, bytes, R]
29
+ StrToBytesSteppedGenerator: ta.TypeAlias = SteppedGenerator[bytes, str, R]
30
+
31
+
32
+ # Stepped reader generators emit either an int or None to request input, or emit some other kind of output.
33
+ SteppedReaderGenerator: ta.TypeAlias = ta.Generator[int | None | O, I | None, R]
34
+
35
+ BytesSteppedReaderGenerator: ta.TypeAlias = SteppedReaderGenerator[bytes, bytes, R]
36
+ StrSteppedReaderGenerator: ta.TypeAlias = SteppedReaderGenerator[str, str, R]
37
+
16
38
 
17
39
  ##
18
40
 
@@ -25,10 +47,8 @@ def flatmap_stepped_generator(
25
47
  terminate: ta.Callable[[OF], bool] | None = None,
26
48
  ) -> ta.Generator[OT, I, lang.Maybe[R]]:
27
49
  """
28
- Given a 'stepped generator' - a generator which accepts input items and yields zero or more non-None values in
29
- response until it signals it's ready for the next input by yielding None - and a function taking a list, returns a
30
- 1:1 generator which accepts input, builds a list of yielded generator output, calls the given function with that
31
- list, and yields the result.
50
+ Given a stepped generator and a function taking a list, returns a direct (1:1) generator which accepts input, builds
51
+ a list of yielded generator output, calls the given function with that list, and yields the result.
32
52
 
33
53
  An optional terminate function may be provided which will cause this function to return early if it returns true for
34
54
  an encountered yielded value. The encountered value causing termination will be included in the list sent to the
@@ -89,16 +109,67 @@ def _is_empty(o: T) -> bool:
89
109
  return len(o) < 1 # type: ignore
90
110
 
91
111
 
112
+ def joined_bytes_stepped_generator(g: BytesSteppedGenerator[R]) -> BytesDirectGenerator[R]:
113
+ return flatmap_stepped_generator(_join_bytes, g, terminate=_is_empty)
114
+
115
+
116
+ def joined_str_stepped_generator(g: StrSteppedGenerator[R]) -> StrDirectGenerator[R]:
117
+ return flatmap_stepped_generator(_join_str, g, terminate=_is_empty)
118
+
119
+
92
120
  ##
93
121
 
94
122
 
95
- def joined_bytes_stepped_generator(
96
- g: ta.Generator[bytes | None, bytes | None, R],
97
- ) -> ta.Generator[bytes, bytes, R]:
98
- return flatmap_stepped_generator(_join_bytes, g, terminate=_is_empty)
123
+ def read_into_bytes_stepped_generator(
124
+ g: BytesSteppedGenerator,
125
+ f: ta.IO,
126
+ *,
127
+ read_size: int = DEFAULT_BUFFER_SIZE,
128
+ ) -> ta.Iterator[bytes]:
129
+ yield from lang.genmap( # type: ignore[misc]
130
+ joined_bytes_stepped_generator(g),
131
+ lang.readiter(f, read_size),
132
+ )
99
133
 
100
134
 
101
- def joined_str_stepped_generator(
102
- g: ta.Generator[str | None, str | None, R],
103
- ) -> ta.Generator[str, str, R]:
104
- return flatmap_stepped_generator(_join_str, g, terminate=_is_empty)
135
+ def read_into_str_stepped_generator(
136
+ g: StrSteppedGenerator,
137
+ f: ta.TextIO,
138
+ *,
139
+ read_size: int = DEFAULT_BUFFER_SIZE,
140
+ ) -> ta.Iterator[str]:
141
+ yield from lang.genmap(
142
+ joined_str_stepped_generator(g),
143
+ lang.readiter(f, read_size),
144
+ )
145
+
146
+
147
+ ##
148
+
149
+
150
+ @lang.autostart
151
+ def buffer_bytes_stepped_reader_generator(g: BytesSteppedReaderGenerator) -> BytesSteppedGenerator:
152
+ o = g.send(None)
153
+ buf: ta.Any = None
154
+
155
+ while True:
156
+ if not buf:
157
+ buf = check.isinstance((yield None), bytes)
158
+
159
+ if o is None or not buf:
160
+ i = buf
161
+ elif isinstance(o, int):
162
+ if len(buf) < o:
163
+ raise NotImplementedError
164
+ i = buf[:o]
165
+ buf = buf[o:]
166
+ else:
167
+ raise TypeError(o)
168
+
169
+ while True:
170
+ o = g.send(i)
171
+ i = None
172
+ if isinstance(o, bytes):
173
+ check.none((yield o))
174
+ else:
175
+ break
omlish/io/pyio.py CHANGED
@@ -4,7 +4,7 @@
4
4
  """
5
5
  Python implementation of the io module.
6
6
 
7
- https://github.com/python/cpython/blob/8fa4dc4ba8646c59f945f2451c53e2919f066065/Lib/_pyio.py
7
+ https://github.com/python/cpython/blob/8b3cccf3f9508572d85b0044519f2bd5715dacad/Lib/_pyio.py
8
8
  """
9
9
  # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
10
10
  # --------------------------------------------
@@ -2580,8 +2580,11 @@ class TextIOWrapper(TextIOBase):
2580
2580
  decoder = self._decoder or self._get_decoder()
2581
2581
 
2582
2582
  if size < 0:
2583
+ chunk = self.buffer.read()
2584
+ if chunk is None:
2585
+ raise BlockingIOError("Read returned None.")
2583
2586
  # Read everything.
2584
- result = self._get_decoded_chars() + decoder.decode(self.buffer.read(), final=True)
2587
+ result = self._get_decoded_chars() + decoder.decode(chunk, final=True)
2585
2588
  if self._snapshot is not None:
2586
2589
  self._set_decoded_chars('')
2587
2590
  self._snapshot = None
omlish/lang/__init__.py CHANGED
@@ -156,6 +156,7 @@ from .iterables import ( # noqa
156
156
  itergen,
157
157
  peek,
158
158
  prodrange,
159
+ readiter,
159
160
  renumerate,
160
161
  take,
161
162
  )
omlish/lang/iterables.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import dataclasses as dc
2
+ import functools
2
3
  import itertools
3
4
  import typing as ta
4
5
 
@@ -46,6 +47,25 @@ def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterable[T]:
46
47
  yield v
47
48
 
48
49
 
50
+ @ta.overload
51
+ def readiter(f: ta.TextIO, sz: int) -> ta.Iterator[str]:
52
+ ...
53
+
54
+
55
+ @ta.overload
56
+ def readiter(f: ta.BinaryIO, sz: int) -> ta.Iterator[bytes]:
57
+ ...
58
+
59
+
60
+ @ta.overload
61
+ def readiter(f: ta.IO, sz: int) -> ta.Iterator[ta.AnyStr]:
62
+ ...
63
+
64
+
65
+ def readiter(f, sz):
66
+ return iter(functools.partial(f.read, sz), None)
67
+
68
+
49
69
  @dc.dataclass(frozen=True)
50
70
  class IterGen(ta.Generic[T]):
51
71
  fn: ta.Callable[[], ta.Iterable[T]]
omlish/lite/marshal.py CHANGED
@@ -251,117 +251,141 @@ _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
251
251
  }
252
252
 
253
253
 
254
- def _make_obj_marshaler(
255
- ty: ta.Any,
256
- rec: ta.Callable[[ta.Any], ObjMarshaler],
257
- *,
258
- nonstrict_dataclasses: bool = False,
259
- ) -> ObjMarshaler:
260
- if isinstance(ty, type):
261
- if abc.ABC in ty.__bases__:
262
- return PolymorphicObjMarshaler.of([ # type: ignore
263
- PolymorphicObjMarshaler.Impl(
264
- ity,
265
- ity.__qualname__,
266
- rec(ity),
267
- )
268
- for ity in deep_subclasses(ty)
269
- if abc.ABC not in ity.__bases__
270
- ])
271
-
272
- if issubclass(ty, enum.Enum):
273
- return EnumObjMarshaler(ty)
274
-
275
- if dc.is_dataclass(ty):
276
- return DataclassObjMarshaler(
277
- ty,
278
- {f.name: rec(f.type) for f in dc.fields(ty)},
279
- nonstrict=nonstrict_dataclasses,
280
- )
281
-
282
- if is_generic_alias(ty):
283
- try:
284
- mt = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES[ta.get_origin(ty)]
285
- except KeyError:
286
- pass
287
- else:
288
- k, v = ta.get_args(ty)
289
- return MappingObjMarshaler(mt, rec(k), rec(v))
290
-
291
- try:
292
- st = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES[ta.get_origin(ty)]
293
- except KeyError:
294
- pass
295
- else:
296
- [e] = ta.get_args(ty)
297
- return IterableObjMarshaler(st, rec(e))
298
-
299
- if is_union_alias(ty):
300
- return OptionalObjMarshaler(rec(get_optional_alias_arg(ty)))
301
-
302
- raise TypeError(ty)
303
-
304
-
305
254
  ##
306
255
 
307
256
 
308
- _OBJ_MARSHALERS_LOCK = threading.RLock()
309
-
310
- _OBJ_MARSHALERS: ta.Dict[ta.Any, ObjMarshaler] = dict(_DEFAULT_OBJ_MARSHALERS)
311
-
312
- _OBJ_MARSHALER_PROXIES: ta.Dict[ta.Any, ProxyObjMarshaler] = {}
313
-
257
+ class ObjMarshalerManager:
258
+ def __init__(
259
+ self,
260
+ *,
261
+ default_obj_marshalers: ta.Dict[ta.Any, ObjMarshaler] = _DEFAULT_OBJ_MARSHALERS, # noqa
262
+ generic_mapping_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES, # noqa
263
+ generic_iterable_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES, # noqa
264
+ ) -> None:
265
+ super().__init__()
266
+
267
+ self._obj_marshalers = dict(default_obj_marshalers)
268
+ self._generic_mapping_types = generic_mapping_types
269
+ self._generic_iterable_types = generic_iterable_types
270
+
271
+ self._lock = threading.RLock()
272
+ self._marshalers: ta.Dict[ta.Any, ObjMarshaler] = dict(_DEFAULT_OBJ_MARSHALERS)
273
+ self._proxies: ta.Dict[ta.Any, ProxyObjMarshaler] = {}
274
+
275
+ #
276
+
277
+ def make_obj_marshaler(
278
+ self,
279
+ ty: ta.Any,
280
+ rec: ta.Callable[[ta.Any], ObjMarshaler],
281
+ *,
282
+ nonstrict_dataclasses: bool = False,
283
+ ) -> ObjMarshaler:
284
+ if isinstance(ty, type):
285
+ if abc.ABC in ty.__bases__:
286
+ return PolymorphicObjMarshaler.of([ # type: ignore
287
+ PolymorphicObjMarshaler.Impl(
288
+ ity,
289
+ ity.__qualname__,
290
+ rec(ity),
291
+ )
292
+ for ity in deep_subclasses(ty)
293
+ if abc.ABC not in ity.__bases__
294
+ ])
295
+
296
+ if issubclass(ty, enum.Enum):
297
+ return EnumObjMarshaler(ty)
298
+
299
+ if dc.is_dataclass(ty):
300
+ return DataclassObjMarshaler(
301
+ ty,
302
+ {f.name: rec(f.type) for f in dc.fields(ty)},
303
+ nonstrict=nonstrict_dataclasses,
304
+ )
314
305
 
315
- def register_opj_marshaler(ty: ta.Any, m: ObjMarshaler) -> None:
316
- with _OBJ_MARSHALERS_LOCK:
317
- if ty in _OBJ_MARSHALERS:
318
- raise KeyError(ty)
319
- _OBJ_MARSHALERS[ty] = m
306
+ if is_generic_alias(ty):
307
+ try:
308
+ mt = self._generic_mapping_types[ta.get_origin(ty)]
309
+ except KeyError:
310
+ pass
311
+ else:
312
+ k, v = ta.get_args(ty)
313
+ return MappingObjMarshaler(mt, rec(k), rec(v))
320
314
 
315
+ try:
316
+ st = self._generic_iterable_types[ta.get_origin(ty)]
317
+ except KeyError:
318
+ pass
319
+ else:
320
+ [e] = ta.get_args(ty)
321
+ return IterableObjMarshaler(st, rec(e))
322
+
323
+ if is_union_alias(ty):
324
+ return OptionalObjMarshaler(rec(get_optional_alias_arg(ty)))
325
+
326
+ raise TypeError(ty)
327
+
328
+ #
329
+
330
+ def register_opj_marshaler(self, ty: ta.Any, m: ObjMarshaler) -> None:
331
+ with self._lock:
332
+ if ty in self._obj_marshalers:
333
+ raise KeyError(ty)
334
+ self._obj_marshalers[ty] = m
335
+
336
+ def get_obj_marshaler(
337
+ self,
338
+ ty: ta.Any,
339
+ *,
340
+ no_cache: bool = False,
341
+ **kwargs: ta.Any,
342
+ ) -> ObjMarshaler:
343
+ with self._lock:
344
+ if not no_cache:
345
+ try:
346
+ return self._obj_marshalers[ty]
347
+ except KeyError:
348
+ pass
321
349
 
322
- def get_obj_marshaler(
323
- ty: ta.Any,
324
- *,
325
- no_cache: bool = False,
326
- **kwargs: ta.Any,
327
- ) -> ObjMarshaler:
328
- with _OBJ_MARSHALERS_LOCK:
329
- if not no_cache:
330
350
  try:
331
- return _OBJ_MARSHALERS[ty]
351
+ return self._proxies[ty]
332
352
  except KeyError:
333
353
  pass
334
354
 
335
- try:
336
- return _OBJ_MARSHALER_PROXIES[ty]
337
- except KeyError:
338
- pass
355
+ rec = functools.partial(
356
+ self.get_obj_marshaler,
357
+ no_cache=no_cache,
358
+ **kwargs,
359
+ )
339
360
 
340
- rec = functools.partial(
341
- get_obj_marshaler,
342
- no_cache=no_cache,
343
- **kwargs,
344
- )
361
+ p = ProxyObjMarshaler()
362
+ self._proxies[ty] = p
363
+ try:
364
+ m = self.make_obj_marshaler(ty, rec, **kwargs)
365
+ finally:
366
+ del self._proxies[ty]
367
+ p.m = m
368
+
369
+ if not no_cache:
370
+ self._obj_marshalers[ty] = m
371
+ return m
372
+
373
+ #
345
374
 
346
- p = ProxyObjMarshaler()
347
- _OBJ_MARSHALER_PROXIES[ty] = p
348
- try:
349
- m = _make_obj_marshaler(ty, rec, **kwargs)
350
- finally:
351
- del _OBJ_MARSHALER_PROXIES[ty]
352
- p.m = m
375
+ def marshal_obj(self, o: ta.Any, ty: ta.Any = None) -> ta.Any:
376
+ return self.get_obj_marshaler(ty if ty is not None else type(o)).marshal(o)
353
377
 
354
- if not no_cache:
355
- _OBJ_MARSHALERS[ty] = m
356
- return m
378
+ def unmarshal_obj(self, o: ta.Any, ty: ta.Union[ta.Type[T], ta.Any]) -> T:
379
+ return self.get_obj_marshaler(ty).unmarshal(o)
357
380
 
358
381
 
359
382
  ##
360
383
 
361
384
 
362
- def marshal_obj(o: ta.Any, ty: ta.Any = None) -> ta.Any:
363
- return get_obj_marshaler(ty if ty is not None else type(o)).marshal(o)
385
+ OBJ_MARSHALER_MANAGER = ObjMarshalerManager()
364
386
 
387
+ register_opj_marshaler = OBJ_MARSHALER_MANAGER.register_opj_marshaler
388
+ get_obj_marshaler = OBJ_MARSHALER_MANAGER.get_obj_marshaler
365
389
 
366
- def unmarshal_obj(o: ta.Any, ty: ta.Union[ta.Type[T], ta.Any]) -> T:
367
- return get_obj_marshaler(ty).unmarshal(o)
390
+ marshal_obj = OBJ_MARSHALER_MANAGER.marshal_obj
391
+ unmarshal_obj = OBJ_MARSHALER_MANAGER.unmarshal_obj
@@ -10,6 +10,19 @@ from .logs import log
10
10
  from .runtime import is_debugger_attached
11
11
 
12
12
 
13
+ SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
14
+
15
+
16
+ ##
17
+
18
+
19
+ SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
20
+ 'pipe': subprocess.PIPE,
21
+ 'stdout': subprocess.STDOUT,
22
+ 'devnull': subprocess.DEVNULL,
23
+ }
24
+
25
+
13
26
  ##
14
27
 
15
28
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev141
3
+ Version: 0.0.0.dev142
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -21,6 +21,7 @@ Requires-Dist: trio-asyncio~=0.15; extra == "all"
21
21
  Requires-Dist: lz4~=4.3; extra == "all"
22
22
  Requires-Dist: python-snappy~=0.7; extra == "all"
23
23
  Requires-Dist: zstandard~=0.23; extra == "all"
24
+ Requires-Dist: brotli~=1.1; extra == "all"
24
25
  Requires-Dist: asttokens~=3.0; extra == "all"
25
26
  Requires-Dist: executing~=2.1; extra == "all"
26
27
  Requires-Dist: psutil~=6.0; extra == "all"
@@ -60,6 +61,7 @@ Provides-Extra: compress
60
61
  Requires-Dist: lz4~=4.3; extra == "compress"
61
62
  Requires-Dist: python-snappy~=0.7; extra == "compress"
62
63
  Requires-Dist: zstandard~=0.23; extra == "compress"
64
+ Requires-Dist: brotli~=1.1; extra == "compress"
63
65
  Provides-Extra: diag
64
66
  Requires-Dist: asttokens~=3.0; extra == "diag"
65
67
  Requires-Dist: executing~=2.1; extra == "diag"