omlish 0.0.0.dev136__py3-none-any.whl → 0.0.0.dev137__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.dev136'
2
- __revision__ = '345b1984bd3c96dc1b3ed4db8402e21d989f38ee'
1
+ __version__ = '0.0.0.dev137'
2
+ __revision__ = '895417d0442f51853415386ace94b1c41da0c990'
3
3
 
4
4
 
5
5
  #
omlish/cached.py CHANGED
@@ -10,9 +10,9 @@ builtins and thus not distinguish it from a normal property.
10
10
 
11
11
  """
12
12
  from .lang.cached import _CachedProperty # noqa
13
- from .lang.cached import cached_function
13
+ from .lang.cached import cached_function as _cached_function
14
14
 
15
- function = cached_function
15
+ function = _cached_function
16
16
 
17
17
  property = property # noqa
18
18
 
@@ -86,7 +86,7 @@ class DynamicTypeMap(ta.Generic[V]):
86
86
  self._items = list(items)
87
87
  self._weak = bool(weak)
88
88
 
89
- self._cache: ta.MutableMapping[type, ta.Any] = weakref.WeakKeyDictionary()
89
+ self._cache: ta.MutableMapping[type, ta.Any] = weakref.WeakKeyDictionary() if weak else {}
90
90
 
91
91
  @property
92
92
  def items(self) -> ta.Sequence[V]:
@@ -3,7 +3,10 @@
3
3
  .venv/bin/python $(curl -LsSf https://raw.githubusercontent.com/wrmsr/omlish/master/omlish/diag/_pycharm/runhack.py -o $(mktemp) && echo "$_") install
4
4
 
5
5
  ==
6
+ TODO:
7
+ - check for existing files - can't run regular dep entrypoints now
6
8
 
9
+ ==
7
10
  See:
8
11
  - https://github.com/JetBrains/intellij-community/blob/6400f70dde6f743e39a257a5a78cc51b644c835e/python/helpers/pycharm/_jb_pytest_runner.py
9
12
  - https://github.com/JetBrains/intellij-community/blob/5a4e584aa59767f2e7cf4bd377adfaaf7503984b/python/helpers/pycharm/_jb_runner_tools.py
@@ -12,7 +12,7 @@ import typing as ta
12
12
  I = ta.TypeVar('I')
13
13
  O = ta.TypeVar('O')
14
14
 
15
- # MachineGen: ta.TypeAlias = ta.Generator[ta.Iterable[O] | None, I, ta.Optional[MachineGen[I, O]]]
15
+ # MachineGen: ta.TypeAlias = ta.Generator[ta.Iterable[O] | None, I | None, ta.Optional[MachineGen[I, O]]]
16
16
  MachineGen: ta.TypeAlias = ta.Generator[ta.Any, ta.Any, ta.Any]
17
17
 
18
18
 
@@ -93,8 +93,10 @@ class GenMachine(ta.Generic[I, O]):
93
93
  if self._gen is None:
94
94
  raise GenMachine.ClosedError
95
95
 
96
+ gi: I | None = i
96
97
  try:
97
- while (o := self._gen.send(i)) is not None:
98
+ while (o := self._gen.send(gi)) is not None:
99
+ gi = None
98
100
  yield from o
99
101
 
100
102
  except StopIteration as s:
File without changes
@@ -0,0 +1,104 @@
1
+ """
2
+ https://docs.python.org/3/library/bz2.html#bz2.BZ2Compressor
3
+ https://docs.python.org/3/library/zlib.html#zlib.decompressobj
4
+ https://docs.python.org/3/library/lzma.html#lzma.LZMADecompressor
5
+ """
6
+ import abc
7
+
8
+
9
+ ##
10
+
11
+
12
+ class Compressor(abc.ABC):
13
+ @abc.abstractmethod
14
+ def compress(self, data: bytes) -> bytes:
15
+ """
16
+ Provide data to the compressor object. Returns a chunk of compressed data if possible, or an empty byte string
17
+ otherwise.
18
+
19
+ When you have finished providing data to the compressor, call the flush() method to finish the compression
20
+ process.
21
+ """
22
+
23
+ raise NotImplementedError
24
+
25
+ @abc.abstractmethod
26
+ def flush(self) -> bytes:
27
+ """
28
+ Finish the compression process. Returns the compressed data left in internal buffers.
29
+
30
+ The compressor object may not be used after this method has been called.
31
+ """
32
+
33
+ raise NotImplementedError
34
+
35
+
36
+ ##
37
+
38
+
39
+ class Decompressor(abc.ABC):
40
+ @property
41
+ @abc.abstractmethod
42
+ def unused_data(self) -> bytes:
43
+ """
44
+ Data found after the end of the compressed stream.
45
+
46
+ If this attribute is accessed before the end of the stream has been reached, its value will be b''.
47
+ """
48
+
49
+ raise NotImplementedError
50
+
51
+ @property
52
+ @abc.abstractmethod
53
+ def eof(self) -> bool:
54
+ """True if the end-of-stream marker has been reached."""
55
+
56
+ raise NotImplementedError
57
+
58
+ @abc.abstractmethod
59
+ def decompress(self, data: bytes, *max_length: int) -> bytes:
60
+ """
61
+ Decompress data, returning a bytes object containing the uncompressed data corresponding to at least part of the
62
+ data in string. This data should be concatenated to the output produced by any preceding calls to the
63
+ decompress() method. Some of the input data may be preserved in internal buffers for later processing.
64
+
65
+ If the optional parameter max_length is non-zero then the return value will be no longer than max_length.
66
+ """
67
+
68
+ raise NotImplementedError
69
+
70
+
71
+ class NeedsInputDecompressor(Decompressor):
72
+ """
73
+ Used by:
74
+ - bz2.BZ2Decompressor
75
+ - lzma.LZMADecompressor
76
+ """
77
+
78
+ @property
79
+ @abc.abstractmethod
80
+ def needs_input(self) -> bool:
81
+ """
82
+ False if the decompress() method can provide more decompressed data before requiring new uncompressed input.
83
+ """
84
+
85
+ raise NotImplementedError
86
+
87
+
88
+ class UnconsumedTailDecompressor(Decompressor):
89
+ """
90
+ Used by:
91
+ - zlib.decompressobj
92
+ """
93
+
94
+ @property
95
+ @abc.abstractmethod
96
+ def unconsumed_tail(self) -> bytes:
97
+ """
98
+ A bytes object that contains any data that was not consumed by the last decompress() call because it exceeded
99
+ the limit for the uncompressed data buffer. This data has not yet been seen by the zlib machinery, so you must
100
+ feed it (possibly with further data concatenated to it) back to a subsequent decompress() method call in order
101
+ to get correct output.
102
+ """
103
+
104
+ raise NotImplementedError
@@ -0,0 +1,147 @@
1
+ # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
2
+ # --------------------------------------------
3
+ #
4
+ # 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization
5
+ # ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated
6
+ # documentation.
7
+ #
8
+ # 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive,
9
+ # royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative
10
+ # works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License
11
+ # Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
12
+ # 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation; All Rights Reserved" are retained in Python
13
+ # alone or in any derivative version prepared by Licensee.
14
+ #
15
+ # 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and
16
+ # wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in
17
+ # any such work a brief summary of the changes made to Python.
18
+ #
19
+ # 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES,
20
+ # EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY
21
+ # OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY
22
+ # RIGHTS.
23
+ #
24
+ # 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
25
+ # DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF
26
+ # ADVISED OF THE POSSIBILITY THEREOF.
27
+ #
28
+ # 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
29
+ #
30
+ # 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint
31
+ # venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade
32
+ # name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
33
+ #
34
+ # 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this
35
+ # License Agreement.
36
+ # ~> https://github.com/python/cpython/blob/f19c50a4817ffebb26132182ed8cec6a72342cc0/Lib/_compression.py
37
+ import typing as ta
38
+
39
+ from ... import check
40
+ from .abc import Compressor
41
+ from .abc import NeedsInputDecompressor
42
+ from .abc import UnconsumedTailDecompressor
43
+ from .types import IncrementalCompressor
44
+ from .types import IncrementalDecompressor
45
+
46
+
47
+ ##
48
+
49
+
50
+ class CompressorIncrementalAdapter:
51
+ def __init__(
52
+ self,
53
+ factory: ta.Callable[..., Compressor],
54
+ ) -> None:
55
+ super().__init__()
56
+
57
+ self._factory = factory
58
+
59
+ def __call__(self) -> IncrementalCompressor:
60
+ compressor = self._factory()
61
+
62
+ while True:
63
+ data = check.isinstance((yield None), bytes)
64
+ if not data:
65
+ break
66
+
67
+ compressed = compressor.compress(data)
68
+ if compressed:
69
+ check.none((yield compressed))
70
+
71
+ if (fl := compressor.flush()):
72
+ check.none((yield fl))
73
+
74
+ check.none((yield b''))
75
+
76
+
77
+ ##
78
+
79
+
80
+ class DecompressorIncrementalAdapter:
81
+ def __init__(
82
+ self,
83
+ factory: ta.Callable[..., NeedsInputDecompressor | UnconsumedTailDecompressor],
84
+ *,
85
+ trailing_error: type[BaseException] | tuple[type[BaseException], ...] = (),
86
+ ) -> None:
87
+ super().__init__()
88
+
89
+ self._factory = factory
90
+ self._trailing_error = trailing_error
91
+
92
+ def __call__(self) -> IncrementalDecompressor:
93
+ pos = 0
94
+
95
+ data = None # Default if EOF is encountered
96
+
97
+ decompressor = self._factory()
98
+
99
+ while True:
100
+ # Depending on the input data, our call to the decompressor may not return any data. In this case, try again
101
+ # after reading another block.
102
+ while True:
103
+ if decompressor.eof:
104
+ rawblock = decompressor.unused_data
105
+ if not rawblock:
106
+ rawblock = check.isinstance((yield None), bytes)
107
+ if not rawblock:
108
+ break
109
+
110
+ # Continue to next stream.
111
+ decompressor = self._factory()
112
+
113
+ try:
114
+ data = decompressor.decompress(rawblock)
115
+ except self._trailing_error:
116
+ # Trailing data isn't a valid compressed stream; ignore it.
117
+ break
118
+
119
+ else:
120
+ if hasattr(decompressor, 'needs_input'):
121
+ if decompressor.needs_input:
122
+ rawblock = check.isinstance((yield None), bytes)
123
+ if not rawblock:
124
+ raise EOFError('Compressed file ended before the end-of-stream marker was reached')
125
+ else:
126
+ rawblock = b''
127
+
128
+ elif hasattr(decompressor, 'unconsumed_tail'):
129
+ if not (rawblock := decompressor.unconsumed_tail):
130
+ rawblock = check.isinstance((yield None), bytes)
131
+ if not rawblock:
132
+ raise EOFError('Compressed file ended before the end-of-stream marker was reached')
133
+
134
+ else:
135
+ raise TypeError(decompressor)
136
+
137
+ data = decompressor.decompress(rawblock)
138
+
139
+ if data:
140
+ break
141
+
142
+ if not data:
143
+ check.none((yield b''))
144
+ return
145
+
146
+ pos += len(data)
147
+ check.none((yield data))
@@ -0,0 +1,41 @@
1
+ import functools
2
+ import typing as ta
3
+
4
+ from ... import lang
5
+ from .adapters import CompressorIncrementalAdapter
6
+ from .adapters import DecompressorIncrementalAdapter
7
+ from .types import IncrementalCompressor
8
+ from .types import IncrementalDecompressor
9
+
10
+
11
+ if ta.TYPE_CHECKING:
12
+ import bz2
13
+ else:
14
+ bz2 = lang.proxy_import('bz2')
15
+
16
+
17
+ class IncrementalBz2Compressor:
18
+ def __init__(
19
+ self,
20
+ *,
21
+ compresslevel: int = 9,
22
+ ) -> None:
23
+ super().__init__()
24
+
25
+ self._compresslevel = compresslevel
26
+
27
+ def __call__(self) -> IncrementalCompressor:
28
+ return CompressorIncrementalAdapter(
29
+ functools.partial(
30
+ bz2.BZ2Compressor, # type: ignore
31
+ self._compresslevel,
32
+ ),
33
+ )()
34
+
35
+
36
+ class IncrementalBz2Decompressor:
37
+ def __call__(self) -> IncrementalDecompressor:
38
+ return DecompressorIncrementalAdapter(
39
+ bz2.BZ2Decompressor, # type: ignore
40
+ trailing_error=OSError,
41
+ )()
@@ -0,0 +1,301 @@
1
+ # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
2
+ # --------------------------------------------
3
+ #
4
+ # 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization
5
+ # ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated
6
+ # documentation.
7
+ #
8
+ # 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive,
9
+ # royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative
10
+ # works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License
11
+ # Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
12
+ # 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation; All Rights Reserved" are retained in Python
13
+ # alone or in any derivative version prepared by Licensee.
14
+ #
15
+ # 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and
16
+ # wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in
17
+ # any such work a brief summary of the changes made to Python.
18
+ #
19
+ # 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES,
20
+ # EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY
21
+ # OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY
22
+ # RIGHTS.
23
+ #
24
+ # 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
25
+ # DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF
26
+ # ADVISED OF THE POSSIBILITY THEREOF.
27
+ #
28
+ # 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
29
+ #
30
+ # 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint
31
+ # venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade
32
+ # name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
33
+ #
34
+ # 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this
35
+ # License Agreement.
36
+ import functools
37
+ import os.path
38
+ import struct
39
+ import time
40
+ import typing as ta
41
+
42
+ from ... import cached
43
+ from ... import check
44
+ from ... import lang
45
+ from ..generators import PrependableBytesGeneratorReader
46
+ from .types import IncrementalCompressor
47
+ from .types import IncrementalDecompressor
48
+
49
+
50
+ if ta.TYPE_CHECKING:
51
+ import gzip
52
+ import zlib
53
+ else:
54
+ gzip = lang.proxy_import('gzip')
55
+ zlib = lang.proxy_import('zlib')
56
+
57
+
58
+ ##
59
+
60
+
61
+ COMPRESS_LEVEL_FAST = 1
62
+ COMPRESS_LEVEL_TRADEOFF = 6
63
+ COMPRESS_LEVEL_BEST = 9
64
+
65
+
66
+ @cached.function
67
+ def _zero_crc() -> int:
68
+ return zlib.crc32(b'')
69
+
70
+
71
+ ##
72
+
73
+
74
+ class IncrementalGzipCompressor:
75
+ def __init__(
76
+ self,
77
+ *,
78
+ compresslevel: int = COMPRESS_LEVEL_BEST,
79
+ name: str | bytes | None = None,
80
+ mtime: float | None = None,
81
+ ) -> None:
82
+ super().__init__()
83
+
84
+ self._name = name or ''
85
+ self._compresslevel = compresslevel
86
+ self._mtime = mtime
87
+
88
+ def _write_gzip_header(self) -> ta.Generator[bytes, None, None]:
89
+ check.none((yield b'\037\213')) # magic header
90
+ check.none((yield b'\010')) # compression method
91
+
92
+ try:
93
+ # RFC 1952 requires the FNAME field to be Latin-1. Do not include filenames that cannot be represented that
94
+ # way.
95
+ fname = os.path.basename(self._name)
96
+ if not isinstance(fname, bytes):
97
+ fname = fname.encode('latin-1')
98
+ if fname.endswith(b'.gz'):
99
+ fname = fname[:-3]
100
+ except UnicodeEncodeError:
101
+ fname = b''
102
+
103
+ flags = 0
104
+ if fname:
105
+ flags = gzip.FNAME
106
+ check.none((yield chr(flags).encode('latin-1')))
107
+
108
+ mtime = self._mtime
109
+ if mtime is None:
110
+ mtime = time.time()
111
+ check.none((yield struct.pack('<L', int(mtime))))
112
+
113
+ if self._compresslevel == COMPRESS_LEVEL_BEST:
114
+ xfl = b'\002'
115
+ elif self._compresslevel == COMPRESS_LEVEL_FAST:
116
+ xfl = b'\004'
117
+ else:
118
+ xfl = b'\000'
119
+ check.none((yield xfl))
120
+
121
+ check.none((yield b'\377'))
122
+
123
+ if fname:
124
+ check.none((yield fname + b'\000'))
125
+
126
+ def __call__(self) -> IncrementalCompressor:
127
+ crc = _zero_crc()
128
+ size = 0
129
+ offset = 0 # Current file offset for seek(), tell(), etc
130
+
131
+ compress = zlib.compressobj(
132
+ self._compresslevel,
133
+ zlib.DEFLATED,
134
+ -zlib.MAX_WBITS,
135
+ zlib.DEF_MEM_LEVEL,
136
+ 0,
137
+ )
138
+
139
+ yield from self._write_gzip_header()
140
+
141
+ while True:
142
+ data: ta.Any = check.isinstance((yield None), bytes)
143
+ if not data:
144
+ break
145
+
146
+ # Called by our self._buffer underlying BufferedWriterDelegate.
147
+ if isinstance(data, (bytes, bytearray)):
148
+ length = len(data)
149
+ else:
150
+ # accept any data that supports the buffer protocol
151
+ data = memoryview(data)
152
+ length = data.nbytes
153
+
154
+ if length > 0:
155
+ if (fl := compress.compress(data)):
156
+ check.none((yield fl))
157
+ size += length
158
+ crc = zlib.crc32(data, crc)
159
+ offset += length
160
+
161
+ if (fl := compress.flush()):
162
+ check.none((yield fl))
163
+
164
+ yield struct.pack('<L', crc)
165
+ # size may exceed 2 GiB, or even 4 GiB
166
+ yield struct.pack('<L', size & 0xffffffff)
167
+
168
+ yield b''
169
+
170
+
171
+ ##
172
+
173
+
174
+ class IncrementalGzipDecompressor:
175
+ def __init__(self) -> None:
176
+ super().__init__()
177
+
178
+ self._factory = functools.partial(
179
+ zlib.decompressobj,
180
+ wbits=-zlib.MAX_WBITS,
181
+ )
182
+
183
+ def _read_gzip_header(
184
+ self,
185
+ rdr: PrependableBytesGeneratorReader,
186
+ ) -> ta.Generator[int | None, bytes, int | None]:
187
+ magic = yield from rdr.read(2)
188
+ if magic == b'':
189
+ return None
190
+
191
+ if magic != b'\037\213':
192
+ raise gzip.BadGzipFile(f'Not a gzipped file ({magic!r})')
193
+
194
+ buf = yield from rdr.read(8)
195
+ method, flag, last_mtime = struct.unpack('<BBIxx', buf)
196
+ if method != 8:
197
+ raise gzip.BadGzipFile('Unknown compression method')
198
+
199
+ if flag & gzip.FEXTRA:
200
+ # Read & discard the extra field, if present
201
+ buf = yield from rdr.read(2)
202
+ extra_len, = struct.unpack('<H', buf)
203
+ if extra_len:
204
+ yield from rdr.read(extra_len)
205
+
206
+ if flag & gzip.FNAME:
207
+ # Read and discard a null-terminated string containing the filename
208
+ while True:
209
+ s = yield from rdr.read(1)
210
+ if not s or s == b'\000':
211
+ break
212
+
213
+ if flag & gzip.FCOMMENT:
214
+ # Read and discard a null-terminated string containing a comment
215
+ while True:
216
+ s = yield from rdr.read(1)
217
+ if not s or s == b'\000':
218
+ break
219
+
220
+ if flag & gzip.FHCRC:
221
+ yield from rdr.read(2) # Read & discard the 16-bit header CRC
222
+
223
+ return last_mtime
224
+
225
+ def _read_eof(
226
+ self,
227
+ rdr: PrependableBytesGeneratorReader,
228
+ crc: int,
229
+ stream_size: int,
230
+ ) -> ta.Generator[int | None, bytes, None]:
231
+ # We've read to the end of the file.
232
+ # We check that the computed CRC and size of the uncompressed data matches the stored values. Note that the size
233
+ # stored is the true file size mod 2**32.
234
+ buf = yield from rdr.read(8)
235
+ crc32, isize = struct.unpack('<II', buf)
236
+ if crc32 != crc:
237
+ raise gzip.BadGzipFile(f'CRC check failed {hex(crc32)} != {hex(crc)}')
238
+ elif isize != (stream_size & 0xffffffff):
239
+ raise gzip.BadGzipFile('Incorrect length of data produced')
240
+
241
+ # Gzip files can be padded with zeroes and still have archives. Consume all zero bytes and set the file position
242
+ # to the first non-zero byte. See http://www.gzip.org/#faq8
243
+ c = b'\0'
244
+ while c == b'\0':
245
+ c = yield from rdr.read(1)
246
+ if c:
247
+ rdr.prepend(c)
248
+
249
+ def __call__(self) -> IncrementalDecompressor:
250
+ rdr = PrependableBytesGeneratorReader()
251
+
252
+ pos = 0 # Current offset in decompressed stream
253
+
254
+ crc = _zero_crc()
255
+ stream_size = 0 # Decompressed size of unconcatenated stream
256
+ new_member = True
257
+
258
+ decompressor = self._factory()
259
+
260
+ while True:
261
+ # For certain input data, a single call to decompress() may not return any data. In this case, retry until
262
+ # we get some data or reach EOF.
263
+ while True:
264
+ if decompressor.eof:
265
+ # Ending case: we've come to the end of a member in the file, so finish up this member, and read a
266
+ # new gzip header. Check the CRC and file size, and set the flag so we read a new member
267
+ yield from self._read_eof(rdr, crc, stream_size)
268
+ new_member = True
269
+ decompressor = self._factory()
270
+
271
+ if new_member:
272
+ # If the _new_member flag is set, we have to jump to the next member, if there is one.
273
+ crc = _zero_crc()
274
+ stream_size = 0 # Decompressed size of unconcatenated stream
275
+ last_mtime = yield from self._read_gzip_header(rdr)
276
+ if not last_mtime:
277
+ check.none((yield b''))
278
+ return
279
+ new_member = False
280
+
281
+ # Read a chunk of data from the file
282
+ if not decompressor.unconsumed_tail:
283
+ buf = yield from rdr.read(None)
284
+ uncompress = decompressor.decompress(buf)
285
+ else:
286
+ uncompress = decompressor.decompress(b'')
287
+
288
+ if decompressor.unused_data != b'':
289
+ # Prepend the already read bytes to the fileobj so they can be seen by _read_eof() and
290
+ # _read_gzip_header()
291
+ rdr.prepend(decompressor.unused_data)
292
+
293
+ if uncompress != b'':
294
+ break
295
+ if buf == b'': # noqa
296
+ raise EOFError('Compressed file ended before the end-of-stream marker was reached')
297
+
298
+ crc = zlib.crc32(uncompress, crc)
299
+ stream_size += len(uncompress)
300
+ pos += len(uncompress)
301
+ check.none((yield uncompress))
@@ -0,0 +1,31 @@
1
+ import typing as ta
2
+
3
+ from ... import lang
4
+ from .adapters import CompressorIncrementalAdapter
5
+ from .adapters import DecompressorIncrementalAdapter
6
+ from .types import IncrementalCompressor
7
+ from .types import IncrementalDecompressor
8
+
9
+
10
+ if ta.TYPE_CHECKING:
11
+ import lzma
12
+ else:
13
+ lzma = lang.proxy_import('lzma')
14
+
15
+
16
+ class IncrementalLzmaCompressor:
17
+ def __init__(self) -> None:
18
+ super().__init__()
19
+
20
+ def __call__(self) -> IncrementalCompressor:
21
+ return CompressorIncrementalAdapter(
22
+ lzma.LZMACompressor, # type: ignore
23
+ )()
24
+
25
+
26
+ class IncrementalLzmaDecompressor:
27
+ def __call__(self) -> IncrementalDecompressor:
28
+ return DecompressorIncrementalAdapter(
29
+ lzma.LZMADecompressor, # type: ignore
30
+ trailing_error=lzma.LZMAError,
31
+ )()
@@ -0,0 +1,29 @@
1
+ # ruff: noqa: UP007
2
+ import typing as ta
3
+
4
+
5
+ IncrementalCompressor: ta.TypeAlias = ta.Generator[
6
+ ta.Union[
7
+ bytes, # Compressed output
8
+ None, # Need input
9
+ ],
10
+ ta.Union[
11
+ bytes, # Input bytes
12
+ None, # Need output
13
+ ],
14
+ None,
15
+ ]
16
+
17
+
18
+ IncrementalDecompressor: ta.TypeAlias = ta.Generator[
19
+ ta.Union[
20
+ bytes, # Uncompressed output
21
+ int, # Need exactly n bytes
22
+ None, # Need any amount of bytes
23
+ ],
24
+ ta.Union[
25
+ bytes, # Input bytes
26
+ None, # Need output
27
+ ],
28
+ None,
29
+ ]
@@ -0,0 +1,50 @@
1
+ """
2
+ TODO:
3
+ - BufferedBytesGeneratorReader
4
+ """
5
+ import typing as ta
6
+
7
+ from .. import check
8
+
9
+
10
+ class PrependableBytesGeneratorReader:
11
+ def __init__(self) -> None:
12
+ super().__init__()
13
+
14
+ self._p: list[bytes] = []
15
+
16
+ def read(self, sz: int | None) -> ta.Generator[int | None, bytes, bytes]:
17
+ if not self._p:
18
+ d = check.isinstance((yield sz), bytes)
19
+ return d
20
+
21
+ if sz is None:
22
+ return self._p.pop(0)
23
+
24
+ l: list[bytes] = []
25
+ r = sz
26
+ while r > 0 and self._p:
27
+ c = self._p[0]
28
+
29
+ if len(c) > r:
30
+ l.append(c[:r])
31
+ self._p[0] = c[r:]
32
+ return b''.join(l)
33
+
34
+ l.append(c)
35
+ r -= len(c)
36
+ self._p.pop(0)
37
+
38
+ if r:
39
+ c = check.isinstance((yield r), bytes)
40
+ if not c:
41
+ return b''
42
+ if len(c) != r:
43
+ raise EOFError(f'Reader got {len(c)} bytes, expected {r}')
44
+ l.append(c)
45
+
46
+ return b''.join(l)
47
+
48
+ def prepend(self, d: bytes) -> None:
49
+ if d:
50
+ self._p.append(d)
omlish/lang/__init__.py CHANGED
@@ -120,6 +120,14 @@ from .functions import ( # noqa
120
120
  void,
121
121
  )
122
122
 
123
+ from .generators import ( # noqa
124
+ CoroutineGenerator,
125
+ Generator,
126
+ GeneratorLike,
127
+ corogen,
128
+ nextgen,
129
+ )
130
+
123
131
  from .imports import ( # noqa
124
132
  can_import,
125
133
  import_all,
@@ -136,7 +144,6 @@ from .imports import ( # noqa
136
144
 
137
145
  from .iterables import ( # noqa
138
146
  BUILTIN_SCALAR_ITERABLE_TYPES,
139
- Generator,
140
147
  asrange,
141
148
  exhaust,
142
149
  flatmap,
@@ -0,0 +1,182 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from .maybes import Maybe
5
+
6
+
7
+ T = ta.TypeVar('T')
8
+ I = ta.TypeVar('I')
9
+ O = ta.TypeVar('O')
10
+ R = ta.TypeVar('R')
11
+ I_contra = ta.TypeVar('I_contra', contravariant=True)
12
+ O_co = ta.TypeVar('O_co', covariant=True)
13
+ R_co = ta.TypeVar('R_co', covariant=True)
14
+
15
+
16
+ ##
17
+
18
+
19
+ def nextgen(g: T) -> T:
20
+ next(g) # type: ignore
21
+ return g
22
+
23
+
24
+ ##
25
+
26
+
27
+ @ta.runtime_checkable
28
+ class GeneratorLike(ta.Protocol[O_co, I_contra, R_co]):
29
+ def send(self, i: I_contra) -> O_co: # Raises[StopIteration[R_co]]
30
+ ...
31
+
32
+ def close(self) -> None:
33
+ ...
34
+
35
+
36
+ class GeneratorLike_(abc.ABC, ta.Generic[O, I, R]): # noqa
37
+ @abc.abstractmethod
38
+ def send(self, i: I) -> O: # Raises[StopIteration[R]]
39
+ raise NotImplementedError
40
+
41
+ def close(self) -> None:
42
+ pass
43
+
44
+
45
+ @ta.overload
46
+ def adapt_generator_like(gl: GeneratorLike_[O, I, R]) -> ta.Generator[O, I, R]:
47
+ ...
48
+
49
+
50
+ @ta.overload
51
+ def adapt_generator_like(gl: GeneratorLike[O, I, R]) -> ta.Generator[O, I, R]:
52
+ ...
53
+
54
+
55
+ def adapt_generator_like(gl):
56
+ try:
57
+ i = yield
58
+ while True:
59
+ i = yield gl.send(i)
60
+ except StopIteration as e:
61
+ return e.value
62
+ finally:
63
+ gl.close()
64
+
65
+
66
+ ##
67
+
68
+
69
+ class Generator(ta.Generator[O, I, R]):
70
+ def __init__(self, g: ta.Generator[O, I, R]) -> None:
71
+ super().__init__()
72
+ self._g = g
73
+
74
+ @property
75
+ def g(self) -> ta.Generator[O, I, R]:
76
+ return self._g
77
+
78
+ value: R
79
+
80
+ def __iter__(self):
81
+ return self
82
+
83
+ def __next__(self):
84
+ try:
85
+ return next(self._g)
86
+ except StopIteration as e:
87
+ self.value = e.value
88
+ raise
89
+
90
+ def send(self, v):
91
+ try:
92
+ return self._g.send(v)
93
+ except StopIteration as e:
94
+ self.value = e.value
95
+ raise
96
+
97
+ def throw(self, *args):
98
+ try:
99
+ return self._g.throw(*args)
100
+ except StopIteration as e:
101
+ self.value = e.value
102
+ raise
103
+
104
+ def close(self):
105
+ self._g.close()
106
+
107
+
108
+ ##
109
+
110
+
111
+ class CoroutineGenerator(ta.Generic[O, I, R]):
112
+ def __init__(self, g: ta.Generator[O, I, R]) -> None:
113
+ super().__init__()
114
+ self._g = g
115
+
116
+ @property
117
+ def g(self) -> ta.Generator[O, I, R]:
118
+ return self._g
119
+
120
+ #
121
+
122
+ def close(self) -> None:
123
+ self._g.close()
124
+
125
+ def __enter__(self) -> ta.Self:
126
+ return self
127
+
128
+ def __exit__(self, exc_type, exc_val, exc_tb):
129
+ self._g.close()
130
+
131
+ #
132
+
133
+ class Output(ta.NamedTuple, ta.Generic[T]):
134
+ v: T
135
+
136
+ @property
137
+ def is_return(self) -> bool:
138
+ raise NotImplementedError
139
+
140
+ class Yield(Output[T]):
141
+ @property
142
+ def is_return(self) -> bool:
143
+ return False
144
+
145
+ class Return(Output[T]):
146
+ @property
147
+ def is_return(self) -> bool:
148
+ return True
149
+
150
+ class Nothing:
151
+ def __new__(cls):
152
+ raise TypeError
153
+
154
+ #
155
+
156
+ def send(self, /, v: I | type[Nothing] = Nothing) -> Yield[O] | Return[R]:
157
+ try:
158
+ if v is self.Nothing:
159
+ o = next(self._g)
160
+ else:
161
+ o = self._g.send(v) # type: ignore[arg-type]
162
+ except StopIteration as e:
163
+ return self.Return(e.value)
164
+ else:
165
+ return self.Yield(o)
166
+
167
+ def send_opt(self, v: I | None) -> Yield[O] | Return[R]:
168
+ return self.send(v if v is not None else self.Nothing)
169
+
170
+ def send_maybe(self, v: Maybe[I]) -> Yield[O] | Return[R]:
171
+ return self.send(v.or_else(self.Nothing))
172
+
173
+ def throw(self, v: BaseException) -> Yield[O] | Return[R]:
174
+ try:
175
+ o = self._g.throw(v)
176
+ except StopIteration as e:
177
+ return self.Return(e.value)
178
+ else:
179
+ return self.Yield(o)
180
+
181
+
182
+ corogen = CoroutineGenerator
omlish/lang/iterables.py CHANGED
@@ -4,7 +4,6 @@ import typing as ta
4
4
 
5
5
 
6
6
  T = ta.TypeVar('T')
7
- S = ta.TypeVar('S')
8
7
  R = ta.TypeVar('R')
9
8
 
10
9
 
@@ -15,6 +14,9 @@ BUILTIN_SCALAR_ITERABLE_TYPES: tuple[type, ...] = (
15
14
  )
16
15
 
17
16
 
17
+ ##
18
+
19
+
18
20
  def ilen(it: ta.Iterable) -> int:
19
21
  c = 0
20
22
  for _ in it:
@@ -44,6 +46,31 @@ def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterable[T]:
44
46
  yield v
45
47
 
46
48
 
49
+ @dc.dataclass(frozen=True)
50
+ class IterGen(ta.Generic[T]):
51
+ fn: ta.Callable[[], ta.Iterable[T]]
52
+
53
+ def __iter__(self):
54
+ return iter(self.fn())
55
+
56
+
57
+ itergen = IterGen
58
+
59
+
60
+ def renumerate(it: ta.Iterable[T]) -> ta.Iterable[tuple[T, int]]:
61
+ return ((e, i) for i, e in enumerate(it))
62
+
63
+
64
+ flatten = itertools.chain.from_iterable
65
+
66
+
67
+ def flatmap(fn: ta.Callable[[T], ta.Iterable[R]], it: ta.Iterable[T]) -> ta.Iterable[R]:
68
+ return flatten(map(fn, it))
69
+
70
+
71
+ ##
72
+
73
+
47
74
  Rangeable: ta.TypeAlias = ta.Union[ # noqa
48
75
  int,
49
76
  tuple[int],
@@ -68,53 +95,3 @@ def prodrange(*dims: Rangeable) -> ta.Iterable[ta.Sequence[int]]:
68
95
  if not dims:
69
96
  return []
70
97
  return itertools.product(*map(asrange, dims))
71
-
72
-
73
- @dc.dataclass(frozen=True)
74
- class itergen(ta.Generic[T]): # noqa
75
- fn: ta.Callable[[], ta.Iterable[T]]
76
-
77
- def __iter__(self):
78
- return iter(self.fn())
79
-
80
-
81
- def renumerate(it: ta.Iterable[T]) -> ta.Iterable[tuple[T, int]]:
82
- return ((e, i) for i, e in enumerate(it))
83
-
84
-
85
- flatten = itertools.chain.from_iterable
86
-
87
-
88
- def flatmap(fn: ta.Callable[[T], ta.Iterable[R]], it: ta.Iterable[T]) -> ta.Iterable[R]:
89
- return flatten(map(fn, it))
90
-
91
-
92
- class Generator(ta.Generator[T, S, R]):
93
- def __init__(self, gen: ta.Generator[T, S, R]) -> None:
94
- super().__init__()
95
- self.gen = gen
96
-
97
- value: R
98
-
99
- def __iter__(self):
100
- return self
101
-
102
- def __next__(self):
103
- return self.send(None)
104
-
105
- def send(self, v):
106
- try:
107
- return self.gen.send(v)
108
- except StopIteration as e:
109
- self.value = e.value
110
- raise
111
-
112
- def throw(self, *args):
113
- try:
114
- return self.gen.throw(*args)
115
- except StopIteration as e:
116
- self.value = e.value
117
- raise
118
-
119
- def close(self):
120
- self.gen.close()
omlish/lang/maybes.py CHANGED
@@ -50,11 +50,11 @@ class Maybe(abc.ABC, ta.Generic[T]):
50
50
  raise NotImplementedError
51
51
 
52
52
  @abc.abstractmethod
53
- def or_else(self, other: T) -> T:
53
+ def or_else(self, other: T | U) -> T | U:
54
54
  raise NotImplementedError
55
55
 
56
56
  @abc.abstractmethod
57
- def or_else_get(self, supplier: ta.Callable[[], T]) -> T:
57
+ def or_else_get(self, supplier: ta.Callable[[], U]) -> T | U:
58
58
  raise NotImplementedError
59
59
 
60
60
  @abc.abstractmethod
@@ -109,10 +109,10 @@ class _Maybe(Maybe[T], tuple):
109
109
  return value
110
110
  return _empty # noqa
111
111
 
112
- def or_else(self, other: T) -> T:
112
+ def or_else(self, other: T | U) -> T | U:
113
113
  return self.must() if self else other
114
114
 
115
- def or_else_get(self, supplier: ta.Callable[[], T]) -> T:
115
+ def or_else_get(self, supplier: ta.Callable[[], T | U]) -> T | U:
116
116
  return self.must() if self else supplier()
117
117
 
118
118
  def or_else_raise(self, exception_supplier: ta.Callable[[], Exception]) -> T:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev136
3
+ Version: 0.0.0.dev137
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,9 +1,9 @@
1
1
  omlish/.manifests.json,sha256=RX24SRc6DCEg77PUVnaXOKCWa5TF_c9RQJdGIf7gl9c,1135
2
- omlish/__about__.py,sha256=CWnfySakfsMZZD27TmuHHIWSs20C8yL_8HidsVhkFwM,3379
2
+ omlish/__about__.py,sha256=keq7273S1vADjELx7wkIYWHvdZy7TM3pyrMqOHXwNYg,3379
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/argparse.py,sha256=cqKGAqcxuxv_s62z0gq29L9KAvg_3-_rFvXKjVpRJjo,8126
5
5
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
6
- omlish/cached.py,sha256=vn3ZiKy0d3NW_kzoXCi_Fg_s5vUcH0LQ1idfKBcQhzk,489
6
+ omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
7
7
  omlish/check.py,sha256=Li5xmecEyWKMzlwWyd6xDHpq3F4lE6IFOPBWdylCnpU,10540
8
8
  omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
9
9
  omlish/defs.py,sha256=T3bq_7h_tO3nDB5RAFBn7DkdeQgqheXzkFColbOHZko,4890
@@ -92,14 +92,14 @@ omlish/bootstrap/main.py,sha256=yZhOHDDlj4xB5a89dRdT8z58FsqqnpoBg1-tvY2CJe4,5903
92
92
  omlish/bootstrap/marshal.py,sha256=ZxdAeMNd2qXRZ1HUK89HmEhz8tqlS9OduW34QBscKw0,516
93
93
  omlish/bootstrap/sys.py,sha256=iLHUNIuIPv-k-Mc6aHj5sSET78olCVt7t0HquFDO4iQ,8762
94
94
  omlish/collections/__init__.py,sha256=zeUvcAz073ekko37QKya6sElTMfKTuF1bKrdbMtaRpI,2142
95
- omlish/collections/_abc.py,sha256=sP7BpTVhx6s6C59mTFeosBi4rHOWC6tbFBYbxdZmvh0,2365
95
+ omlish/collections/abc.py,sha256=sP7BpTVhx6s6C59mTFeosBi4rHOWC6tbFBYbxdZmvh0,2365
96
96
  omlish/collections/coerce.py,sha256=o11AMrUiyoadd8WkdqeKPIpXf2xd0LyylzNCyJivCLU,7036
97
97
  omlish/collections/exceptions.py,sha256=shcS-NCnEUudF8qC_SmO2TQyjivKlS4TDjaz_faqQ0c,44
98
98
  omlish/collections/frozen.py,sha256=DGxemj_pVID85tSBm-Wns_x4ov0wOEIT6X5bVgJtmkA,4152
99
99
  omlish/collections/hasheq.py,sha256=XcOCE6f2lXizDCOXxSX6vJv-rLcpDo2OWCYIKGSWuic,3697
100
100
  omlish/collections/identity.py,sha256=jhEpC8tnfh3Sg-MJff1Fp9eMydt150wits_UeVdctUk,2723
101
101
  omlish/collections/indexed.py,sha256=tFQsIWH4k9QqsF5VB7DsIVNsRThiQNx-ooRF36X_PnU,2203
102
- omlish/collections/mappings.py,sha256=eEifLZez-BWunTuj6974bGxBFfNRODZpu6Oa7_2ME94,3190
102
+ omlish/collections/mappings.py,sha256=laXV4WU1VZPGwaJQsJCQsmL3BVeUfELljTZ6a5sfg0s,3206
103
103
  omlish/collections/ordered.py,sha256=RzEC3fHvrDeJQSWThVDNYQKke263Vje1II5YwtDwT1Q,2335
104
104
  omlish/collections/persistent.py,sha256=KG471s0bhhReQrjlmX0xaN9HeAIcrtT264ddZCxsExo,875
105
105
  omlish/collections/skiplist.py,sha256=xjuKZtSScp1VnOi9lpf7I090vGp1DnjA5ELjFhMeGps,5987
@@ -156,7 +156,7 @@ omlish/diag/pycharm.py,sha256=7-r_F-whXt8v-0dehxAX-MeMFPM3iZXX9IfeL0GfUtk,4643
156
156
  omlish/diag/pydevd.py,sha256=UN55ZjkWLCVyHxE2CNRRYamuvSKfzWsn0D5oczRTXO4,7536
157
157
  omlish/diag/threads.py,sha256=1-x02VCDZ407gfbtXm1pWK-ubqhqfePm9PMqkHCVoqk,3642
158
158
  omlish/diag/_pycharm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
159
- omlish/diag/_pycharm/runhack.py,sha256=kuRgiMYYsM0wkHwJfbN2ao79OFxmfjV1vkGaEspLKRA,35080
159
+ omlish/diag/_pycharm/runhack.py,sha256=JFz4GVN4AXndJo38iwnK5X_vH2MS6wyO8YmVZss5YRE,35157
160
160
  omlish/diag/replserver/__init__.py,sha256=uLo6V2aQ29v9z3IMELlPDSlG3_2iOT4-_X8VniF-EgE,235
161
161
  omlish/diag/replserver/__main__.py,sha256=LmU41lQ58bm1h4Mx7S8zhE_uEBSC6kPcp9mn5JRpulA,32
162
162
  omlish/diag/replserver/console.py,sha256=XzBDVhYlr8FY6ym4OwoaIHuFOHnGK3dTYlMDIOMUUlA,7410
@@ -198,7 +198,7 @@ omlish/formats/json/stream/lex.py,sha256=_JYBFnAyHsw_3hu8I0rvZqSSkRCU1BvQzgO81Kf
198
198
  omlish/formats/json/stream/parse.py,sha256=s21PgiuNTcqc_i9QS1ggmEp8Qwp_hOqtosr5d0zpg_o,5204
199
199
  omlish/formats/json/stream/render.py,sha256=NtmDsN92xZi5dkgSSuMeMXMAiJblmjz1arB4Ft7vBhc,3715
200
200
  omlish/funcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
- omlish/funcs/genmachine.py,sha256=RlU-y_dt2nRdvoo7Z3HsUELlBn3KuyI4qUnqLVbChRI,2450
201
+ omlish/funcs/genmachine.py,sha256=XEHy8SFgHCDYSAqlRm-7wlYQX2h6UWehR2_uMw9EOXU,2509
202
202
  omlish/funcs/match.py,sha256=gMLZn1enNiFvQaWrQubY300M1BrmdKWzeePihBS7Ywc,6153
203
203
  omlish/funcs/pairs.py,sha256=OzAwnALkRJXVpD47UvBZHKzQfHtFNry_EgjTcC7vgLU,10606
204
204
  omlish/funcs/pipes.py,sha256=E7Sz8Aj8ke_vCs5AMNwg1I36kRdHVGTnzxVQaDyn43U,2490
@@ -256,10 +256,18 @@ omlish/inject/impl/providers.py,sha256=QnwhsujJFIHC0JTgd2Wlo1kP53i3CWTrj1nKU2DNx
256
256
  omlish/inject/impl/proxy.py,sha256=1ko0VaKqzu9UG8bIldp9xtUrAVUOFTKWKTjOCqIGr4s,1636
257
257
  omlish/inject/impl/scopes.py,sha256=hKnzNieB-fJSFEXDP_QG1mCfIKoVFIfFlf9LiIt5tk4,5920
258
258
  omlish/io/__init__.py,sha256=aaIEsXTSfytW-oEkUWczdUJ_ifFY7ihIpyidIbfjkwY,56
259
- omlish/io/_abc.py,sha256=Cxs8KB1B_69rxpUYxI-MTsilAmNooJJn3w07DKqYKkE,1255
259
+ omlish/io/abc.py,sha256=Cxs8KB1B_69rxpUYxI-MTsilAmNooJJn3w07DKqYKkE,1255
260
+ omlish/io/generators.py,sha256=ZlAp_t0ZD_aKztlio1i_hezmpIFFjaiXtrnY6-2QsPs,1123
260
261
  omlish/io/pyio.py,sha256=YB3g6yg64MzcFwbzKBo4adnbsbZ3FZMlOZfjNtWmYoc,95316
261
262
  omlish/io/trampoline.py,sha256=oUKTQg1F5xQS1431Kt7MbK-NZpX509ubcXU-s86xJr8,7171
262
- omlish/lang/__init__.py,sha256=f7FZLBp3n_edtP5EBK2NoEOi01MIgVs51Z-MbmLsLR8,3760
263
+ omlish/io/compress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
264
+ omlish/io/compress/abc.py,sha256=R9ebpSjJK4VAimV3OevPJB-jSDTGB_xi2FKNZKbTdYE,3054
265
+ omlish/io/compress/adapters.py,sha256=wS7cA_quham3C23j3_H6sf2EQ4gI0vURTQdPhapiiFE,6088
266
+ omlish/io/compress/bz2.py,sha256=XxpAKdQ5pdWfa23a0F6ZspU_GxHhyJVVPjv2SCyw4hM,1006
267
+ omlish/io/compress/gzip.py,sha256=TUjbE5cjiHXd2ZMsRgXBlSW3mCF7xMt_NvIRTYddBCQ,11054
268
+ omlish/io/compress/lzma.py,sha256=rM6FXWeD6s-K-Sfxn04AaFILS-v4rliWKlPnAl_RrJw,803
269
+ omlish/io/compress/types.py,sha256=IuCyxFX8v12fGqCq2ofCCRM5ZM-4zngHeeBW_PWqYbM,557
270
+ omlish/lang/__init__.py,sha256=U6-WtzQL48e9wqWHmxuA1X2PoGlx6Di2HSAi13gTwjw,3866
263
271
  omlish/lang/cached.py,sha256=92TvRZQ6sWlm7dNn4hgl7aWKbX0J1XUEo3DRjBpgVQk,7834
264
272
  omlish/lang/clsdct.py,sha256=AjtIWLlx2E6D5rC97zQ3Lwq2SOMkbg08pdO_AxpzEHI,1744
265
273
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
@@ -268,9 +276,10 @@ omlish/lang/datetimes.py,sha256=ehI_DhQRM-bDxAavnp470XcekbbXc4Gdw9y1KpHDJT0,223
268
276
  omlish/lang/descriptors.py,sha256=RRBbkMgTzg82fFFE4D0muqobpM-ZZaOta6yB1lpX3s8,6617
269
277
  omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
270
278
  omlish/lang/functions.py,sha256=kkPfcdocg-OmyN7skIqrFxNvqAv89Zc_kXKYAN8vw8g,3895
279
+ omlish/lang/generators.py,sha256=AShh0x-9Z9qolAYEOZJgYJcxQuyA3HKq0c9tLwNcFs4,3766
271
280
  omlish/lang/imports.py,sha256=TXLbj2F53LsmozlM05bQhvow9kEgWJOi9qYKsnm2D18,9258
272
- omlish/lang/iterables.py,sha256=xRwktm6i2RHSb_ELfAXdjITIfE69qDyMEzgeZqvQXiU,2386
273
- omlish/lang/maybes.py,sha256=es84ECP0D9exVT9WF7MNlKKuCev-T56oUx92oTABkfU,3389
281
+ omlish/lang/iterables.py,sha256=1bc-Vn-b34T6Gy3li2tMNYpUvuwCC7fjg7dpjXkTfWY,1746
282
+ omlish/lang/maybes.py,sha256=1RN7chX_x2XvgUwryZRz0W7hAX-be3eEFcFub5vvf6M,3417
274
283
  omlish/lang/objects.py,sha256=LOC3JvX1g5hPxJ7Sv2TK9kNkAo9c8J-Jw2NmClR_rkA,4576
275
284
  omlish/lang/resolving.py,sha256=OuN2mDTPNyBUbcrswtvFKtj4xgH4H4WglgqSKv3MTy0,1606
276
285
  omlish/lang/resources.py,sha256=yywDWhh0tsgw24l7mHYv49ll4oZS8Kc8MSCa8f4UbbI,2280
@@ -324,7 +333,7 @@ omlish/lite/http/handlers.py,sha256=Yu0P3nqz-frklwCM2PbiWvoJNE-NqeTFLBvpNpqcdtA,
324
333
  omlish/lite/http/parsing.py,sha256=jLdbBTQQhKU701j3_Ixl77nQE3rZld2qbJNAFhuW_cc,13977
325
334
  omlish/lite/http/versions.py,sha256=M6WhZeeyun-3jL_NCViNONOfLCiApuFOfe5XNJwzSvw,394
326
335
  omlish/logs/__init__.py,sha256=FbOyAW-lGH8gyBlSVArwljdYAU6RnwZLI5LwAfuNnrk,438
327
- omlish/logs/_abc.py,sha256=rWySJcr1vatu-AR1EYtODRhi-TjFaixqUzXeWg1c0GA,8006
336
+ omlish/logs/abc.py,sha256=rWySJcr1vatu-AR1EYtODRhi-TjFaixqUzXeWg1c0GA,8006
328
337
  omlish/logs/configs.py,sha256=EE0jlNaXJbGnM7V-y4xS5VwyTBSTzFzc0BYaVjg0JmA,1283
329
338
  omlish/logs/formatters.py,sha256=q79nMnR2mRIStPyGrydQHpYTXgC5HHptt8lH3W2Wwbs,671
330
339
  omlish/logs/handlers.py,sha256=UpzUf3kWBBzWOnrtljoZsLjISw3Ix-ePz3Nsmp6lRgE,255
@@ -419,7 +428,7 @@ omlish/specs/openapi/__init__.py,sha256=zilQhafjvteRDF_TUIRgF293dBC6g-TJChmUb6T9
419
428
  omlish/specs/openapi/marshal.py,sha256=Z-E2Knm04C81N8AA8cibCVSl2ImhSpHZVc7yAhmPx88,2135
420
429
  omlish/specs/openapi/openapi.py,sha256=y4h04jeB7ORJSVrcy7apaBdpwLjIyscv1Ub5SderH2c,12682
421
430
  omlish/sql/__init__.py,sha256=TpZLsEJKJzvJ0eMzuV8hwOJJbkxBCV1RZPUMLAVB6io,173
422
- omlish/sql/_abc.py,sha256=kiOitW_ZhTXrJanJ582wD7o9K69v6HXqDPkxuHEAxrc,1606
431
+ omlish/sql/abc.py,sha256=kiOitW_ZhTXrJanJ582wD7o9K69v6HXqDPkxuHEAxrc,1606
423
432
  omlish/sql/dbapi.py,sha256=5ghJH-HexsmDlYdWlhf00nCGQX2IC98_gxIxMkucOas,3195
424
433
  omlish/sql/dbs.py,sha256=lpdFmm2vTwLoBiVYGj9yPsVcTEYYNCxlYZZpjfChzkY,1870
425
434
  omlish/sql/params.py,sha256=Z4VPet6GhNqD1T_MXSWSHkdy3cpUEhST-OplC4B_fYI,4433
@@ -480,9 +489,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
480
489
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
481
490
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
482
491
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
483
- omlish-0.0.0.dev136.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
484
- omlish-0.0.0.dev136.dist-info/METADATA,sha256=heiHEjvH_MYIlgwIfTWaObhQg178414yEPPo1_ZbNjU,4173
485
- omlish-0.0.0.dev136.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
486
- omlish-0.0.0.dev136.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
487
- omlish-0.0.0.dev136.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
488
- omlish-0.0.0.dev136.dist-info/RECORD,,
492
+ omlish-0.0.0.dev137.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
493
+ omlish-0.0.0.dev137.dist-info/METADATA,sha256=iU_JPwXqYl0APiFu32-g6dTXny6kbXZWslNaWevwHEs,4173
494
+ omlish-0.0.0.dev137.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
495
+ omlish-0.0.0.dev137.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
496
+ omlish-0.0.0.dev137.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
497
+ omlish-0.0.0.dev137.dist-info/RECORD,,
File without changes
File without changes
File without changes
File without changes