omlish 0.0.0.dev133__py3-none-any.whl → 0.0.0.dev177__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +5 -3
  3. omlish/antlr/_runtime/__init__.py +0 -22
  4. omlish/antlr/_runtime/_all.py +24 -0
  5. omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
  6. omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
  7. omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
  8. omlish/antlr/_runtime/xpath/XPath.py +7 -1
  9. omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
  10. omlish/antlr/delimit.py +106 -0
  11. omlish/antlr/dot.py +31 -0
  12. omlish/antlr/errors.py +11 -0
  13. omlish/antlr/input.py +96 -0
  14. omlish/antlr/parsing.py +19 -0
  15. omlish/antlr/runtime.py +102 -0
  16. omlish/antlr/utils.py +38 -0
  17. omlish/argparse/all.py +45 -0
  18. omlish/{argparse.py → argparse/cli.py} +112 -107
  19. omlish/asyncs/__init__.py +0 -35
  20. omlish/asyncs/all.py +35 -0
  21. omlish/asyncs/asyncio/all.py +7 -0
  22. omlish/asyncs/asyncio/channels.py +40 -0
  23. omlish/asyncs/asyncio/streams.py +45 -0
  24. omlish/asyncs/asyncio/subprocesses.py +238 -0
  25. omlish/asyncs/asyncio/timeouts.py +16 -0
  26. omlish/asyncs/bluelet/LICENSE +6 -0
  27. omlish/asyncs/bluelet/all.py +67 -0
  28. omlish/asyncs/bluelet/api.py +23 -0
  29. omlish/asyncs/bluelet/core.py +178 -0
  30. omlish/asyncs/bluelet/events.py +78 -0
  31. omlish/asyncs/bluelet/files.py +80 -0
  32. omlish/asyncs/bluelet/runner.py +416 -0
  33. omlish/asyncs/bluelet/sockets.py +214 -0
  34. omlish/bootstrap/sys.py +3 -3
  35. omlish/cached.py +2 -2
  36. omlish/check.py +49 -460
  37. omlish/codecs/__init__.py +72 -0
  38. omlish/codecs/base.py +106 -0
  39. omlish/codecs/bytes.py +119 -0
  40. omlish/codecs/chain.py +23 -0
  41. omlish/codecs/funcs.py +39 -0
  42. omlish/codecs/registry.py +139 -0
  43. omlish/codecs/standard.py +4 -0
  44. omlish/codecs/text.py +217 -0
  45. omlish/collections/cache/impl.py +50 -57
  46. omlish/collections/coerce.py +1 -0
  47. omlish/collections/mappings.py +1 -1
  48. omlish/configs/flattening.py +1 -1
  49. omlish/defs.py +1 -1
  50. omlish/diag/_pycharm/runhack.py +8 -2
  51. omlish/diag/procfs.py +8 -8
  52. omlish/docker/__init__.py +0 -36
  53. omlish/docker/all.py +31 -0
  54. omlish/docker/consts.py +4 -0
  55. omlish/{lite/docker.py → docker/detect.py} +18 -0
  56. omlish/docker/{helpers.py → timebomb.py} +0 -21
  57. omlish/formats/cbor.py +31 -0
  58. omlish/formats/cloudpickle.py +31 -0
  59. omlish/formats/codecs.py +93 -0
  60. omlish/formats/json/codecs.py +29 -0
  61. omlish/formats/json/delimted.py +4 -0
  62. omlish/formats/json/stream/errors.py +2 -0
  63. omlish/formats/json/stream/lex.py +12 -6
  64. omlish/formats/json/stream/parse.py +38 -22
  65. omlish/formats/json5.py +31 -0
  66. omlish/formats/pickle.py +31 -0
  67. omlish/formats/repr.py +25 -0
  68. omlish/formats/toml.py +17 -0
  69. omlish/formats/yaml.py +25 -0
  70. omlish/funcs/__init__.py +0 -0
  71. omlish/{genmachine.py → funcs/genmachine.py} +5 -4
  72. omlish/{matchfns.py → funcs/match.py} +1 -1
  73. omlish/funcs/pairs.py +215 -0
  74. omlish/http/__init__.py +0 -48
  75. omlish/http/all.py +48 -0
  76. omlish/http/coro/__init__.py +0 -0
  77. omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
  78. omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
  79. omlish/{lite/http → http}/handlers.py +3 -2
  80. omlish/{lite/http → http}/parsing.py +1 -0
  81. omlish/http/sessions.py +1 -1
  82. omlish/{lite/http → http}/versions.py +1 -0
  83. omlish/inject/managed.py +2 -2
  84. omlish/io/__init__.py +0 -3
  85. omlish/{lite/io.py → io/buffers.py} +8 -9
  86. omlish/io/compress/__init__.py +9 -0
  87. omlish/io/compress/abc.py +104 -0
  88. omlish/io/compress/adapters.py +148 -0
  89. omlish/io/compress/base.py +24 -0
  90. omlish/io/compress/brotli.py +47 -0
  91. omlish/io/compress/bz2.py +61 -0
  92. omlish/io/compress/codecs.py +78 -0
  93. omlish/io/compress/gzip.py +350 -0
  94. omlish/io/compress/lz4.py +91 -0
  95. omlish/io/compress/lzma.py +81 -0
  96. omlish/io/compress/snappy.py +34 -0
  97. omlish/io/compress/zlib.py +74 -0
  98. omlish/io/compress/zstd.py +44 -0
  99. omlish/io/fdio/__init__.py +1 -0
  100. omlish/{lite → io}/fdio/handlers.py +5 -5
  101. omlish/{lite → io}/fdio/kqueue.py +8 -8
  102. omlish/{lite → io}/fdio/manager.py +7 -7
  103. omlish/{lite → io}/fdio/pollers.py +13 -13
  104. omlish/io/generators/__init__.py +56 -0
  105. omlish/io/generators/consts.py +1 -0
  106. omlish/io/generators/direct.py +13 -0
  107. omlish/io/generators/readers.py +189 -0
  108. omlish/io/generators/stepped.py +191 -0
  109. omlish/io/pyio.py +5 -2
  110. omlish/iterators/__init__.py +24 -0
  111. omlish/iterators/iterators.py +132 -0
  112. omlish/iterators/recipes.py +18 -0
  113. omlish/iterators/tools.py +96 -0
  114. omlish/iterators/unique.py +67 -0
  115. omlish/lang/__init__.py +13 -1
  116. omlish/lang/functions.py +11 -2
  117. omlish/lang/generators.py +243 -0
  118. omlish/lang/iterables.py +46 -49
  119. omlish/lang/maybes.py +4 -4
  120. omlish/lite/cached.py +39 -6
  121. omlish/lite/check.py +438 -75
  122. omlish/lite/contextmanagers.py +17 -4
  123. omlish/lite/dataclasses.py +42 -0
  124. omlish/lite/inject.py +28 -45
  125. omlish/lite/logs.py +0 -270
  126. omlish/lite/marshal.py +309 -144
  127. omlish/lite/pycharm.py +47 -0
  128. omlish/lite/reflect.py +33 -0
  129. omlish/lite/resources.py +8 -0
  130. omlish/lite/runtime.py +4 -4
  131. omlish/lite/shlex.py +12 -0
  132. omlish/lite/socketserver.py +2 -2
  133. omlish/lite/strings.py +31 -0
  134. omlish/logs/__init__.py +0 -32
  135. omlish/logs/{_abc.py → abc.py} +0 -1
  136. omlish/logs/all.py +37 -0
  137. omlish/logs/{formatters.py → color.py} +1 -2
  138. omlish/logs/configs.py +7 -38
  139. omlish/logs/filters.py +10 -0
  140. omlish/logs/handlers.py +4 -1
  141. omlish/logs/json.py +56 -0
  142. omlish/logs/proxy.py +99 -0
  143. omlish/logs/standard.py +128 -0
  144. omlish/logs/utils.py +2 -2
  145. omlish/manifests/__init__.py +2 -0
  146. omlish/manifests/load.py +209 -0
  147. omlish/manifests/types.py +17 -0
  148. omlish/marshal/base.py +1 -1
  149. omlish/marshal/factories.py +1 -1
  150. omlish/marshal/forbidden.py +1 -1
  151. omlish/marshal/iterables.py +1 -1
  152. omlish/marshal/literals.py +50 -0
  153. omlish/marshal/mappings.py +1 -1
  154. omlish/marshal/maybes.py +1 -1
  155. omlish/marshal/standard.py +5 -1
  156. omlish/marshal/unions.py +1 -1
  157. omlish/os/__init__.py +0 -0
  158. omlish/os/atomics.py +205 -0
  159. omlish/os/deathsig.py +23 -0
  160. omlish/{os.py → os/files.py} +0 -9
  161. omlish/{lite → os}/journald.py +2 -1
  162. omlish/os/linux.py +484 -0
  163. omlish/os/paths.py +36 -0
  164. omlish/{lite → os}/pidfile.py +1 -0
  165. omlish/os/sizes.py +9 -0
  166. omlish/reflect/__init__.py +3 -0
  167. omlish/reflect/subst.py +2 -1
  168. omlish/reflect/types.py +126 -44
  169. omlish/secrets/pwhash.py +1 -1
  170. omlish/secrets/subprocesses.py +3 -1
  171. omlish/specs/jsonrpc/marshal.py +1 -1
  172. omlish/specs/openapi/marshal.py +1 -1
  173. omlish/sql/alchemy/asyncs.py +1 -1
  174. omlish/sql/queries/__init__.py +9 -1
  175. omlish/sql/queries/building.py +3 -0
  176. omlish/sql/queries/exprs.py +10 -27
  177. omlish/sql/queries/idents.py +48 -10
  178. omlish/sql/queries/names.py +80 -13
  179. omlish/sql/queries/params.py +64 -0
  180. omlish/sql/queries/rendering.py +1 -1
  181. omlish/subprocesses.py +340 -0
  182. omlish/term.py +29 -14
  183. omlish/testing/pytest/marks.py +2 -2
  184. omlish/testing/pytest/plugins/asyncs.py +6 -1
  185. omlish/testing/pytest/plugins/logging.py +1 -1
  186. omlish/testing/pytest/plugins/switches.py +1 -1
  187. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
  188. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
  189. omlish/fnpairs.py +0 -496
  190. omlish/formats/json/cli/__main__.py +0 -11
  191. omlish/formats/json/cli/cli.py +0 -298
  192. omlish/formats/json/cli/formats.py +0 -71
  193. omlish/formats/json/cli/io.py +0 -74
  194. omlish/formats/json/cli/parsing.py +0 -82
  195. omlish/formats/json/cli/processing.py +0 -48
  196. omlish/formats/json/cli/rendering.py +0 -92
  197. omlish/iterators.py +0 -300
  198. omlish/lite/subprocesses.py +0 -130
  199. /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
  200. /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
  201. /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
  202. /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
  203. /omlish/collections/{_abc.py → abc.py} +0 -0
  204. /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
  205. /omlish/io/{_abc.py → abc.py} +0 -0
  206. /omlish/sql/{_abc.py → abc.py} +0 -0
  207. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,61 @@
1
+ import dataclasses as dc
2
+ import functools
3
+ import typing as ta
4
+
5
+ from ... import lang
6
+ from ..generators import BytesSteppedGenerator
7
+ from ..generators import BytesSteppedReaderGenerator
8
+ from .adapters import CompressorObjectIncrementalAdapter
9
+ from .adapters import DecompressorObjectIncrementalAdapter
10
+ from .base import Compression
11
+ from .base import IncrementalCompression
12
+ from .codecs import make_compression_codec
13
+ from .codecs import make_compression_lazy_loaded_codec
14
+
15
+
16
+ if ta.TYPE_CHECKING:
17
+ import bz2
18
+ else:
19
+ bz2 = lang.proxy_import('bz2')
20
+
21
+
22
+ ##
23
+
24
+
25
+ @dc.dataclass(frozen=True, kw_only=True)
26
+ class Bz2Compression(Compression, IncrementalCompression):
27
+ level: int = 9
28
+
29
+ def compress(self, d: bytes) -> bytes:
30
+ return bz2.compress(
31
+ d,
32
+ self.level,
33
+ )
34
+
35
+ def decompress(self, d: bytes) -> bytes:
36
+ return bz2.decompress(
37
+ d,
38
+ )
39
+
40
+ def compress_incremental(self) -> BytesSteppedGenerator[None]:
41
+ return lang.nextgen(CompressorObjectIncrementalAdapter(
42
+ functools.partial(
43
+ bz2.BZ2Compressor, # type: ignore
44
+ self.level,
45
+ ),
46
+ )())
47
+
48
+ def decompress_incremental(self) -> BytesSteppedReaderGenerator[None]:
49
+ return DecompressorObjectIncrementalAdapter(
50
+ bz2.BZ2Decompressor, # type: ignore
51
+ trailing_error=OSError,
52
+ )()
53
+
54
+
55
+ ##
56
+
57
+
58
+ BZ2_CODEC = make_compression_codec('bz2', Bz2Compression)
59
+
60
+ # @omlish-manifest
61
+ _BZ2_LAZY_CODEC = make_compression_lazy_loaded_codec(__name__, 'BZ2_CODEC', BZ2_CODEC)
@@ -0,0 +1,78 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from ... import codecs
5
+ from ..generators import buffer_bytes_stepped_reader_generator
6
+ from .base import Compression
7
+ from .base import IncrementalCompression
8
+
9
+
10
+ ##
11
+
12
+
13
+ @dc.dataclass(frozen=True)
14
+ class CompressionEagerCodec(codecs.EagerCodec[bytes, bytes]):
15
+ compression: Compression
16
+
17
+ def encode(self, i: bytes) -> bytes:
18
+ return self.compression.compress(i)
19
+
20
+ def decode(self, o: bytes) -> bytes:
21
+ return self.compression.decompress(o)
22
+
23
+
24
+ ##
25
+
26
+
27
+ @dc.dataclass(frozen=True)
28
+ class CompressionIncrementalCodec(codecs.IncrementalCodec[bytes, bytes]):
29
+ compression: IncrementalCompression
30
+
31
+ def encode_incremental(self) -> ta.Generator[bytes | None, bytes, None]:
32
+ return self.compression.compress_incremental()
33
+
34
+ def decode_incremental(self) -> ta.Generator[bytes | None, bytes, None]:
35
+ return buffer_bytes_stepped_reader_generator(self.compression.decompress_incremental())
36
+
37
+
38
+ ##
39
+
40
+
41
+ class CompressionCodec(codecs.Codec):
42
+ pass
43
+
44
+
45
+ def make_compression_codec(
46
+ name: str,
47
+ cls: type[Compression],
48
+ *,
49
+ aliases: ta.Collection[str] | None = None,
50
+ ) -> CompressionCodec:
51
+ return CompressionCodec(
52
+ name=name,
53
+ aliases=aliases,
54
+
55
+ input=bytes,
56
+ output=bytes,
57
+
58
+ new=lambda *args, **kwargs: CompressionEagerCodec(cls(*args, **kwargs)),
59
+
60
+ new_incremental=(
61
+ lambda *args, **kwargs: CompressionIncrementalCodec(cls(*args, **kwargs)) # noqa
62
+ ) if issubclass(cls, IncrementalCompression) else None,
63
+ )
64
+
65
+
66
+ ##
67
+
68
+
69
+ def make_compression_lazy_loaded_codec(
70
+ mod_name: str,
71
+ attr_name: str,
72
+ codec: CompressionCodec,
73
+ ) -> codecs.LazyLoadedCodec:
74
+ return codecs.LazyLoadedCodec.new(
75
+ mod_name,
76
+ attr_name,
77
+ codec,
78
+ )
@@ -0,0 +1,350 @@
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 dataclasses as dc
37
+ import functools
38
+ import os.path
39
+ import struct
40
+ import time
41
+ import typing as ta
42
+
43
+ from ... import cached
44
+ from ... import check
45
+ from ... import lang
46
+ from ..generators import BytesSteppedGenerator
47
+ from ..generators import BytesSteppedReaderGenerator
48
+ from ..generators.readers import PrependableBytesGeneratorReader
49
+ from .base import Compression
50
+ from .base import IncrementalCompression
51
+ from .codecs import make_compression_codec
52
+ from .codecs import make_compression_lazy_loaded_codec
53
+
54
+
55
+ if ta.TYPE_CHECKING:
56
+ import gzip
57
+ import zlib
58
+ else:
59
+ gzip = lang.proxy_import('gzip')
60
+ zlib = lang.proxy_import('zlib')
61
+
62
+
63
+ ##
64
+
65
+
66
+ COMPRESS_LEVEL_FAST = 1
67
+ COMPRESS_LEVEL_TRADEOFF = 6
68
+ COMPRESS_LEVEL_BEST = 9
69
+
70
+
71
+ @dc.dataclass(frozen=True, kw_only=True)
72
+ class GzipCompression(Compression, IncrementalCompression):
73
+ level: int = COMPRESS_LEVEL_BEST
74
+
75
+ mtime: float | None = None
76
+
77
+ def compress(self, d: bytes) -> bytes:
78
+ return gzip.compress(
79
+ d,
80
+ self.level,
81
+ mtime=self.mtime,
82
+ )
83
+
84
+ def decompress(self, d: bytes) -> bytes:
85
+ return gzip.decompress(
86
+ d,
87
+ )
88
+
89
+ def compress_incremental(self) -> BytesSteppedGenerator[None]:
90
+ return lang.nextgen(IncrementalGzipCompressor(
91
+ level=self.level,
92
+ mtime=self.mtime,
93
+ )())
94
+
95
+ def decompress_incremental(self) -> BytesSteppedReaderGenerator[None]:
96
+ return IncrementalGzipDecompressor()()
97
+
98
+
99
+ ##
100
+
101
+
102
+ @cached.function
103
+ def _zero_crc() -> int:
104
+ return zlib.crc32(b'')
105
+
106
+
107
+ ##
108
+
109
+
110
+ class IncrementalGzipCompressor:
111
+ def __init__(
112
+ self,
113
+ *,
114
+ level: int = COMPRESS_LEVEL_BEST,
115
+ name: str | bytes | None = None,
116
+ mtime: float | None = None,
117
+ ) -> None:
118
+ super().__init__()
119
+
120
+ self._name = name or ''
121
+ self._level = level
122
+ self._mtime = mtime
123
+
124
+ def _write_gzip_header(self) -> ta.Generator[bytes, None, None]:
125
+ check.none((yield b'\037\213')) # magic header
126
+ check.none((yield b'\010')) # compression method
127
+
128
+ try:
129
+ # RFC 1952 requires the FNAME field to be Latin-1. Do not include filenames that cannot be represented that
130
+ # way.
131
+ fname = os.path.basename(self._name)
132
+ if not isinstance(fname, bytes):
133
+ fname = fname.encode('latin-1')
134
+ if fname.endswith(b'.gz'):
135
+ fname = fname[:-3]
136
+ except UnicodeEncodeError:
137
+ fname = b''
138
+
139
+ flags = 0
140
+ if fname:
141
+ flags = gzip.FNAME
142
+ check.none((yield chr(flags).encode('latin-1')))
143
+
144
+ mtime = self._mtime
145
+ if mtime is None:
146
+ mtime = time.time()
147
+ check.none((yield struct.pack('<L', int(mtime))))
148
+
149
+ if self._level == COMPRESS_LEVEL_BEST:
150
+ xfl = b'\002'
151
+ elif self._level == COMPRESS_LEVEL_FAST:
152
+ xfl = b'\004'
153
+ else:
154
+ xfl = b'\000'
155
+ check.none((yield xfl))
156
+
157
+ check.none((yield b'\377'))
158
+
159
+ if fname:
160
+ check.none((yield fname + b'\000'))
161
+
162
+ def __call__(self) -> BytesSteppedGenerator:
163
+ crc = _zero_crc()
164
+ size = 0
165
+ offset = 0 # Current file offset for seek(), tell(), etc
166
+ wrote_header = False
167
+
168
+ compress = zlib.compressobj(
169
+ self._level,
170
+ zlib.DEFLATED,
171
+ -zlib.MAX_WBITS,
172
+ zlib.DEF_MEM_LEVEL,
173
+ 0,
174
+ )
175
+
176
+ while True:
177
+ data: ta.Any = check.isinstance((yield None), bytes)
178
+
179
+ if not wrote_header:
180
+ yield from self._write_gzip_header()
181
+ wrote_header = True
182
+
183
+ if not data:
184
+ break
185
+
186
+ # Called by our self._buffer underlying BufferedWriterDelegate.
187
+ if isinstance(data, (bytes, bytearray)):
188
+ length = len(data)
189
+ else:
190
+ # accept any data that supports the buffer protocol
191
+ data = memoryview(data)
192
+ length = data.nbytes
193
+
194
+ if length > 0:
195
+ if (fl := compress.compress(data)):
196
+ check.none((yield fl))
197
+ size += length
198
+ crc = zlib.crc32(data, crc)
199
+ offset += length
200
+
201
+ if (fl := compress.flush()):
202
+ check.none((yield fl))
203
+
204
+ yield struct.pack('<L', crc)
205
+ # size may exceed 2 GiB, or even 4 GiB
206
+ yield struct.pack('<L', size & 0xffffffff)
207
+
208
+ yield b''
209
+
210
+
211
+ ##
212
+
213
+
214
+ class IncrementalGzipDecompressor:
215
+ def __init__(self) -> None:
216
+ super().__init__()
217
+
218
+ self._factory = functools.partial(
219
+ zlib.decompressobj,
220
+ wbits=-zlib.MAX_WBITS,
221
+ )
222
+
223
+ def _read_gzip_header(
224
+ self,
225
+ rdr: PrependableBytesGeneratorReader,
226
+ ) -> ta.Generator[int | None, bytes, int | None]:
227
+ magic = yield from rdr.read(2)
228
+ if magic == b'':
229
+ return None
230
+
231
+ if magic != b'\037\213':
232
+ raise gzip.BadGzipFile(f'Not a gzipped file ({magic!r})')
233
+
234
+ buf = yield from rdr.read(8)
235
+ method, flag, last_mtime = struct.unpack('<BBIxx', buf)
236
+ if method != 8:
237
+ raise gzip.BadGzipFile('Unknown compression method')
238
+
239
+ if flag & gzip.FEXTRA:
240
+ # Read & discard the extra field, if present
241
+ buf = yield from rdr.read(2)
242
+ extra_len, = struct.unpack('<H', buf)
243
+ if extra_len:
244
+ yield from rdr.read(extra_len)
245
+
246
+ if flag & gzip.FNAME:
247
+ # Read and discard a null-terminated string containing the filename
248
+ while True:
249
+ s = yield from rdr.read(1)
250
+ if not s or s == b'\000':
251
+ break
252
+
253
+ if flag & gzip.FCOMMENT:
254
+ # Read and discard a null-terminated string containing a comment
255
+ while True:
256
+ s = yield from rdr.read(1)
257
+ if not s or s == b'\000':
258
+ break
259
+
260
+ if flag & gzip.FHCRC:
261
+ yield from rdr.read(2) # Read & discard the 16-bit header CRC
262
+
263
+ return last_mtime
264
+
265
+ def _read_eof(
266
+ self,
267
+ rdr: PrependableBytesGeneratorReader,
268
+ crc: int,
269
+ stream_size: int,
270
+ ) -> ta.Generator[int | None, bytes, None]:
271
+ # We've read to the end of the file.
272
+ # We check that the computed CRC and size of the uncompressed data matches the stored values. Note that the size
273
+ # stored is the true file size mod 2**32.
274
+ buf = yield from rdr.read(8)
275
+ crc32, isize = struct.unpack('<II', buf)
276
+ if crc32 != crc:
277
+ raise gzip.BadGzipFile(f'CRC check failed {hex(crc32)} != {hex(crc)}')
278
+ elif isize != (stream_size & 0xffffffff):
279
+ raise gzip.BadGzipFile('Incorrect length of data produced')
280
+
281
+ # Gzip files can be padded with zeroes and still have archives. Consume all zero bytes and set the file position
282
+ # to the first non-zero byte. See http://www.gzip.org/#faq8
283
+ c = b'\0'
284
+ while c == b'\0':
285
+ c = yield from rdr.read(1)
286
+ if c:
287
+ rdr.prepend(c)
288
+
289
+ def __call__(self) -> BytesSteppedReaderGenerator:
290
+ rdr = PrependableBytesGeneratorReader()
291
+
292
+ pos = 0 # Current offset in decompressed stream
293
+
294
+ crc = _zero_crc()
295
+ stream_size = 0 # Decompressed size of unconcatenated stream
296
+ new_member = True
297
+
298
+ decompressor = self._factory()
299
+
300
+ while True:
301
+ # For certain input data, a single call to decompress() may not return any data. In this case, retry until
302
+ # we get some data or reach EOF.
303
+ while True:
304
+ if decompressor.eof:
305
+ # Ending case: we've come to the end of a member in the file, so finish up this member, and read a
306
+ # new gzip header. Check the CRC and file size, and set the flag so we read a new member
307
+ yield from self._read_eof(rdr, crc, stream_size)
308
+ new_member = True
309
+ decompressor = self._factory()
310
+
311
+ if new_member:
312
+ # If the _new_member flag is set, we have to jump to the next member, if there is one.
313
+ crc = _zero_crc()
314
+ stream_size = 0 # Decompressed size of unconcatenated stream
315
+ last_mtime = yield from self._read_gzip_header(rdr)
316
+ if not last_mtime:
317
+ check.none((yield b''))
318
+ return
319
+ new_member = False
320
+
321
+ # Read a chunk of data from the file
322
+ if not decompressor.unconsumed_tail:
323
+ buf = yield from rdr.read(None)
324
+ uncompress = decompressor.decompress(buf)
325
+ else:
326
+ uncompress = decompressor.decompress(b'')
327
+
328
+ if decompressor.unused_data != b'':
329
+ # Prepend the already read bytes to the fileobj so they can be seen by _read_eof() and
330
+ # _read_gzip_header()
331
+ rdr.prepend(decompressor.unused_data)
332
+
333
+ if uncompress != b'':
334
+ break
335
+ if buf == b'': # noqa
336
+ raise EOFError('Compressed file ended before the end-of-stream marker was reached')
337
+
338
+ crc = zlib.crc32(uncompress, crc)
339
+ stream_size += len(uncompress)
340
+ pos += len(uncompress)
341
+ check.none((yield uncompress))
342
+
343
+
344
+ ##
345
+
346
+
347
+ GZIP_CODEC = make_compression_codec('gzip', GzipCompression, aliases=['gz'])
348
+
349
+ # @omlish-manifest
350
+ _GZIP_LAZY_CODEC = make_compression_lazy_loaded_codec(__name__, 'GZIP_CODEC', GZIP_CODEC)
@@ -0,0 +1,91 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from ... import check
5
+ from ... import lang
6
+ from ..generators import BytesSteppedGenerator
7
+ from .base import Compression
8
+ from .base import IncrementalCompression
9
+ from .codecs import make_compression_codec
10
+ from .codecs import make_compression_lazy_loaded_codec
11
+
12
+
13
+ if ta.TYPE_CHECKING:
14
+ import lz4.frame as lz4_frame
15
+ else:
16
+ lz4_frame = lang.proxy_import('lz4.frame')
17
+
18
+
19
+ ##
20
+
21
+
22
+ @dc.dataclass(frozen=True, kw_only=True)
23
+ class Lz4Compression(Compression, IncrementalCompression):
24
+ level: int = 0
25
+
26
+ block_size: int = 0
27
+ block_linked: bool = True
28
+ block_checksum: bool = False
29
+ content_checksum: bool = False
30
+ store_size: bool = True
31
+ auto_flush: bool = False
32
+
33
+ def compress(self, d: bytes) -> bytes:
34
+ return lz4_frame.compress(
35
+ d,
36
+ compression_level=self.level,
37
+ block_size=self.block_size,
38
+ content_checksum=self.content_checksum,
39
+ block_linked=self.block_linked,
40
+ store_size=self.store_size,
41
+ )
42
+
43
+ def decompress(self, d: bytes) -> bytes:
44
+ return lz4_frame.decompress(
45
+ d,
46
+ )
47
+
48
+ @lang.autostart
49
+ def compress_incremental(self) -> BytesSteppedGenerator[None]:
50
+ with lz4_frame.LZ4FrameCompressor(
51
+ compression_level=self.level,
52
+ block_size=self.block_size,
53
+ block_linked=self.block_linked,
54
+ block_checksum=self.block_checksum,
55
+ content_checksum=self.content_checksum,
56
+ auto_flush=self.auto_flush,
57
+ ) as compressor:
58
+ started = False
59
+ while True:
60
+ i = check.isinstance((yield None), bytes)
61
+ if not started:
62
+ yield compressor.begin()
63
+ started = True
64
+ if not i:
65
+ yield compressor.flush()
66
+ yield b''
67
+ return
68
+ if (o := compressor.compress(i)):
69
+ yield o
70
+
71
+ @lang.autostart
72
+ def decompress_incremental(self) -> BytesSteppedGenerator[None]:
73
+ # lz4 lib does internal buffering so this is simply a BytesSteppedGenerator not a BytesSteppedReaderGenerator as
74
+ # it only yields None, accepting any number of bytes at a time.
75
+ with lz4_frame.LZ4FrameDecompressor() as decompressor:
76
+ while True:
77
+ i = check.isinstance((yield None), bytes)
78
+ if not i:
79
+ yield b''
80
+ return
81
+ if (o := decompressor.decompress(i)):
82
+ yield o
83
+
84
+
85
+ ##
86
+
87
+
88
+ LZ4_CODEC = make_compression_codec('lz4', Lz4Compression)
89
+
90
+ # @omlish-manifest
91
+ _LZ4_LAZY_CODEC = make_compression_lazy_loaded_codec(__name__, 'LZ4_CODEC', LZ4_CODEC)
@@ -0,0 +1,81 @@
1
+ import dataclasses as dc
2
+ import functools
3
+ import typing as ta
4
+
5
+ from ... import lang
6
+ from ..generators import BytesSteppedGenerator
7
+ from ..generators import BytesSteppedReaderGenerator
8
+ from .adapters import CompressorObjectIncrementalAdapter
9
+ from .adapters import DecompressorObjectIncrementalAdapter
10
+ from .base import Compression
11
+ from .base import IncrementalCompression
12
+ from .codecs import make_compression_codec
13
+ from .codecs import make_compression_lazy_loaded_codec
14
+
15
+
16
+ if ta.TYPE_CHECKING:
17
+ import lzma
18
+ else:
19
+ lzma = lang.proxy_import('lzma')
20
+
21
+
22
+ ##
23
+
24
+
25
+ @dc.dataclass(frozen=True, kw_only=True)
26
+ class LzmaCompression(Compression, IncrementalCompression):
27
+ format: int | None = None
28
+
29
+ check: int = -1
30
+ preset: int | None = None
31
+ filters: dict | None = None
32
+
33
+ mem_limit: int | None = None
34
+
35
+ def compress(self, d: bytes) -> bytes:
36
+ return lzma.compress(
37
+ d,
38
+ format=self.format if self.format is not None else lzma.FORMAT_XZ,
39
+ check=self.check,
40
+ preset=self.preset,
41
+ filters=self.filters, # type: ignore[arg-type]
42
+ )
43
+
44
+ def decompress(self, d: bytes) -> bytes:
45
+ return lzma.decompress(
46
+ d,
47
+ format=self.format if self.format is not None else lzma.FORMAT_AUTO,
48
+ memlimit=self.mem_limit,
49
+ filters=self.filters, # type: ignore[arg-type]
50
+ )
51
+
52
+ def compress_incremental(self) -> BytesSteppedGenerator[None]:
53
+ return lang.nextgen(CompressorObjectIncrementalAdapter(
54
+ functools.partial( # type: ignore
55
+ lzma.LZMACompressor,
56
+ format=self.format if self.format is not None else lzma.FORMAT_XZ,
57
+ check=self.check,
58
+ preset=self.preset,
59
+ filters=self.filters, # type: ignore[arg-type]
60
+ ),
61
+ )())
62
+
63
+ def decompress_incremental(self) -> BytesSteppedReaderGenerator[None]:
64
+ return DecompressorObjectIncrementalAdapter(
65
+ functools.partial( # type: ignore
66
+ lzma.LZMADecompressor,
67
+ format=self.format if self.format is not None else lzma.FORMAT_AUTO,
68
+ memlimit=self.mem_limit,
69
+ filters=self.filters, # type: ignore[arg-type]
70
+ ),
71
+ trailing_error=lzma.LZMAError,
72
+ )()
73
+
74
+
75
+ ##
76
+
77
+
78
+ LZMA_CODEC = make_compression_codec('lzma', LzmaCompression)
79
+
80
+ # @omlish-manifest
81
+ _LZMA_LAZY_CODEC = make_compression_lazy_loaded_codec(__name__, 'LZMA_CODEC', LZMA_CODEC)
@@ -0,0 +1,34 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from ... import lang
5
+ from .base import Compression
6
+ from .codecs import make_compression_codec
7
+ from .codecs import make_compression_lazy_loaded_codec
8
+
9
+
10
+ if ta.TYPE_CHECKING:
11
+ import snappy
12
+ else:
13
+ snappy = lang.proxy_import('snappy')
14
+
15
+
16
+ ##
17
+
18
+
19
+ @dc.dataclass(frozen=True)
20
+ class SnappyCompression(Compression):
21
+ def compress(self, d: bytes) -> bytes:
22
+ return snappy.compress(d)
23
+
24
+ def decompress(self, d: bytes) -> bytes:
25
+ return snappy.decompress(d)
26
+
27
+
28
+ ##
29
+
30
+
31
+ SNAPPY_CODEC = make_compression_codec('snappy', SnappyCompression)
32
+
33
+ # @omlish-manifest
34
+ _SNAPPY_LAZY_CODEC = make_compression_lazy_loaded_codec(__name__, 'SNAPPY_CODEC', SNAPPY_CODEC)