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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -1,19 +1,18 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
1
3
  import socket
2
4
  import typing as ta
3
5
 
4
- from ..check import check_isinstance
5
- from ..check import check_none
6
- from ..check import check_not_none
7
- from ..check import check_state
8
- from ..http.coroserver import CoroHttpServer
9
- from ..http.handlers import HttpHandler
10
- from ..io import IncrementalWriteBuffer
11
- from ..io import ReadableListBuffer
12
- from ..socket import SocketAddress
13
- from .handlers import SocketFdIoHandler
6
+ from ...io.buffers import IncrementalWriteBuffer
7
+ from ...io.buffers import ReadableListBuffer
8
+ from ...io.fdio.handlers import SocketFdioHandler
9
+ from ...lite.check import check
10
+ from ...lite.socket import SocketAddress
11
+ from ..handlers import HttpHandler
12
+ from .server import CoroHttpServer
14
13
 
15
14
 
16
- class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
15
+ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
17
16
  def __init__(
18
17
  self,
19
18
  addr: SocketAddress,
@@ -22,14 +21,16 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
22
21
  *,
23
22
  read_size: int = 0x10000,
24
23
  write_size: int = 0x10000,
24
+ log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
25
25
  ) -> None:
26
- check_state(not sock.getblocking())
26
+ check.state(not sock.getblocking())
27
27
 
28
28
  super().__init__(addr, sock)
29
29
 
30
30
  self._handler = handler
31
31
  self._read_size = read_size
32
32
  self._write_size = write_size
33
+ self._log_handler = log_handler
33
34
 
34
35
  self._read_buf = ReadableListBuffer()
35
36
  self._write_buf: IncrementalWriteBuffer | None = None
@@ -46,7 +47,7 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
46
47
  #
47
48
 
48
49
  def _next_io(self) -> None: # noqa
49
- coro = check_not_none(self._srv_coro)
50
+ coro = check.not_none(self._srv_coro)
50
51
 
51
52
  d: bytes | None = None
52
53
  o = self._cur_io
@@ -64,7 +65,8 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
64
65
  break
65
66
 
66
67
  if isinstance(o, CoroHttpServer.AnyLogIo):
67
- print(o)
68
+ if self._log_handler is not None:
69
+ self._log_handler(self._coro_srv, o)
68
70
  o = None
69
71
 
70
72
  elif isinstance(o, CoroHttpServer.ReadIo):
@@ -78,7 +80,7 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
78
80
  o = None
79
81
 
80
82
  elif isinstance(o, CoroHttpServer.WriteIo):
81
- check_none(self._write_buf)
83
+ check.none(self._write_buf)
82
84
  self._write_buf = IncrementalWriteBuffer(o.data, write_size=self._write_size)
83
85
  break
84
86
 
@@ -99,7 +101,7 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
99
101
 
100
102
  def on_readable(self) -> None:
101
103
  try:
102
- buf = check_not_none(self._sock).recv(self._read_size)
104
+ buf = check.not_none(self._sock).recv(self._read_size)
103
105
  except BlockingIOError:
104
106
  return
105
107
  except ConnectionResetError:
@@ -115,12 +117,12 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
115
117
  self._next_io()
116
118
 
117
119
  def on_writable(self) -> None:
118
- check_isinstance(self._cur_io, CoroHttpServer.WriteIo)
119
- wb = check_not_none(self._write_buf)
120
+ check.isinstance(self._cur_io, CoroHttpServer.WriteIo)
121
+ wb = check.not_none(self._write_buf)
120
122
  while wb.rem > 0:
121
123
  def send(d: bytes) -> int:
122
124
  try:
123
- return check_not_none(self._sock).send(d)
125
+ return check.not_none(self._sock).send(d)
124
126
  except ConnectionResetError:
125
127
  self.close()
126
128
  return 0
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
3
4
  # --------------------------------------------
4
5
  #
@@ -62,20 +63,18 @@ import textwrap
62
63
  import time
63
64
  import typing as ta
64
65
 
65
- from ..check import check_isinstance
66
- from ..check import check_none
67
- from ..check import check_not_none
68
- from ..socket import SocketAddress
69
- from ..socket import SocketHandler
70
- from .handlers import HttpHandler
71
- from .handlers import HttpHandlerRequest
72
- from .handlers import UnsupportedMethodHttpHandlerError
73
- from .parsing import EmptyParsedHttpResult
74
- from .parsing import HttpRequestParser
75
- from .parsing import ParsedHttpRequest
76
- from .parsing import ParseHttpRequestError
77
- from .versions import HttpProtocolVersion
78
- from .versions import HttpProtocolVersions
66
+ from ...lite.check import check
67
+ from ...lite.socket import SocketAddress
68
+ from ...lite.socket import SocketHandler
69
+ from ..handlers import HttpHandler
70
+ from ..handlers import HttpHandlerRequest
71
+ from ..handlers import UnsupportedMethodHttpHandlerError
72
+ from ..parsing import EmptyParsedHttpResult
73
+ from ..parsing import HttpRequestParser
74
+ from ..parsing import ParsedHttpRequest
75
+ from ..parsing import ParseHttpRequestError
76
+ from ..versions import HttpProtocolVersion
77
+ from ..versions import HttpProtocolVersions
79
78
 
80
79
 
81
80
  CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
@@ -406,13 +405,13 @@ class CoroHttpServer:
406
405
  yield o
407
406
 
408
407
  elif isinstance(o, self.AnyReadIo):
409
- i = check_isinstance((yield o), bytes)
408
+ i = check.isinstance((yield o), bytes)
410
409
 
411
410
  elif isinstance(o, self._Response):
412
411
  i = None
413
412
  r = self._preprocess_response(o)
414
413
  b = self._build_response_bytes(r)
415
- check_none((yield self.WriteIo(b)))
414
+ check.none((yield self.WriteIo(b)))
416
415
 
417
416
  else:
418
417
  raise TypeError(o)
@@ -435,7 +434,7 @@ class CoroHttpServer:
435
434
  sz = next(gen)
436
435
  while True:
437
436
  try:
438
- line = check_isinstance((yield self.ReadLineIo(sz)), bytes)
437
+ line = check.isinstance((yield self.ReadLineIo(sz)), bytes)
439
438
  sz = gen.send(line)
440
439
  except StopIteration as e:
441
440
  parsed = e.value
@@ -454,11 +453,11 @@ class CoroHttpServer:
454
453
  yield self._build_error_response(err)
455
454
  return
456
455
 
457
- parsed = check_isinstance(parsed, ParsedHttpRequest)
456
+ parsed = check.isinstance(parsed, ParsedHttpRequest)
458
457
 
459
458
  # Log
460
459
 
461
- check_none((yield self.ParsedRequestLogIo(parsed)))
460
+ check.none((yield self.ParsedRequestLogIo(parsed)))
462
461
 
463
462
  # Handle CONTINUE
464
463
 
@@ -474,7 +473,7 @@ class CoroHttpServer:
474
473
 
475
474
  request_data: ta.Optional[bytes]
476
475
  if (cl := parsed.headers.get('Content-Length')) is not None:
477
- request_data = check_isinstance((yield self.ReadIo(int(cl))), bytes)
476
+ request_data = check.isinstance((yield self.ReadIo(int(cl))), bytes)
478
477
  else:
479
478
  request_data = None
480
479
 
@@ -482,7 +481,7 @@ class CoroHttpServer:
482
481
 
483
482
  handler_request = HttpHandlerRequest(
484
483
  client_address=self._client_address,
485
- method=check_not_none(parsed.method),
484
+ method=check.not_none(parsed.method),
486
485
  path=parsed.path,
487
486
  headers=parsed.headers,
488
487
  data=request_data,
@@ -1,13 +1,14 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  import dataclasses as dc
3
4
  import http.server
4
5
  import typing as ta
5
6
 
6
- from ..socket import SocketAddress
7
+ from ..lite.socket import SocketAddress
7
8
  from .parsing import HttpHeaders
8
9
 
9
10
 
10
- HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse']
11
+ HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.TypeAlias
11
12
 
12
13
 
13
14
  @dc.dataclass(frozen=True)
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
3
4
  # --------------------------------------------
4
5
  #
omlish/http/sessions.py CHANGED
@@ -8,9 +8,9 @@ import time
8
8
  import typing as ta
9
9
  import zlib
10
10
 
11
- from .. import fnpairs as fpa
12
11
  from .. import lang
13
12
  from .. import secrets as sec
13
+ from ..funcs import pairs as fpa
14
14
  from .cookies import dump_cookie
15
15
  from .cookies import parse_cookie
16
16
  from .json import JSON_TAGGER
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  import typing as ta
3
4
 
4
5
 
omlish/inject/managed.py CHANGED
@@ -14,9 +14,9 @@ from .injector import create_injector
14
14
 
15
15
 
16
16
  if ta.TYPE_CHECKING:
17
- from .. import asyncs as _asyncs
17
+ from ..asyncs import all as _asyncs
18
18
  else:
19
- _asyncs = lang.proxy_import('..asyncs', __package__)
19
+ _asyncs = lang.proxy_import('..asyncs.all', __package__)
20
20
 
21
21
 
22
22
  T = ta.TypeVar('T')
omlish/io/__init__.py CHANGED
@@ -1,3 +0,0 @@
1
- from ..lite.io import ( # noqa
2
- DelimitingBuffer,
3
- )
@@ -1,11 +1,10 @@
1
1
  # ruff: noqa: UP007
2
+ # @omlish-lite
2
3
  import io
3
4
  import typing as ta
4
5
 
5
- from .check import check_isinstance
6
- from .check import check_non_empty
7
- from .check import check_not_none
8
- from .strings import attr_repr
6
+ from ..lite.check import check
7
+ from ..lite.strings import attr_repr
9
8
 
10
9
 
11
10
  class DelimitingBuffer:
@@ -39,7 +38,7 @@ class DelimitingBuffer:
39
38
  ) -> None:
40
39
  super().__init__()
41
40
 
42
- self._delimiters = frozenset(check_isinstance(d, int) for d in delimiters)
41
+ self._delimiters = frozenset(check.isinstance(d, int) for d in delimiters)
43
42
  self._keep_ends = keep_ends
44
43
  self._max_size = max_size
45
44
 
@@ -70,7 +69,7 @@ class DelimitingBuffer:
70
69
  return r
71
70
 
72
71
  def _append_and_reset(self, chunk: bytes) -> bytes:
73
- buf = check_not_none(self._buf)
72
+ buf = check.not_none(self._buf)
74
73
  if not buf.tell():
75
74
  return chunk
76
75
 
@@ -192,7 +191,7 @@ class IncrementalWriteBuffer:
192
191
  ) -> None:
193
192
  super().__init__()
194
193
 
195
- check_non_empty(data)
194
+ check.not_empty(data)
196
195
  self._len = len(data)
197
196
  self._write_size = write_size
198
197
 
@@ -207,11 +206,11 @@ class IncrementalWriteBuffer:
207
206
  return self._len - self._pos
208
207
 
209
208
  def write(self, fn: ta.Callable[[bytes], int]) -> int:
210
- lst = check_non_empty(self._lst)
209
+ lst = check.not_empty(self._lst)
211
210
 
212
211
  t = 0
213
212
  for i, d in enumerate(lst): # noqa
214
- n = fn(check_non_empty(d))
213
+ n = fn(check.not_empty(d))
215
214
  if not n:
216
215
  break
217
216
  t += n
@@ -0,0 +1,9 @@
1
+ """
2
+ TODO:
3
+ - zipfile?
4
+ - tarfile?
5
+ - lzf?
6
+ - blosc?
7
+ - python-lzo?
8
+ - zfp -> fp8?
9
+ """
@@ -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 CompressorObject(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 DecompressorObject(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 NeedsInputDecompressorObject(DecompressorObject):
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 UnconsumedTailDecompressorObject(DecompressorObject):
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,148 @@
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 ..generators import BytesSteppedGenerator
41
+ from ..generators import BytesSteppedReaderGenerator
42
+ from .abc import CompressorObject
43
+ from .abc import NeedsInputDecompressorObject
44
+ from .abc import UnconsumedTailDecompressorObject
45
+
46
+
47
+ ##
48
+
49
+
50
+ class CompressorObjectIncrementalAdapter:
51
+ def __init__(
52
+ self,
53
+ factory: ta.Callable[..., CompressorObject],
54
+ ) -> None:
55
+ super().__init__()
56
+
57
+ self._factory = factory
58
+
59
+ def __call__(self) -> BytesSteppedGenerator:
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 DecompressorObjectIncrementalAdapter:
81
+ def __init__(
82
+ self,
83
+ factory: ta.Callable[..., NeedsInputDecompressorObject | UnconsumedTailDecompressorObject],
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) -> BytesSteppedReaderGenerator:
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))
148
+ data = None
@@ -0,0 +1,24 @@
1
+ import abc
2
+
3
+ from ..generators import BytesSteppedGenerator
4
+ from ..generators import BytesSteppedReaderGenerator
5
+
6
+
7
+ class Compression(abc.ABC):
8
+ @abc.abstractmethod
9
+ def compress(self, d: bytes) -> bytes:
10
+ raise NotImplementedError
11
+
12
+ @abc.abstractmethod
13
+ def decompress(self, d: bytes) -> bytes:
14
+ raise NotImplementedError
15
+
16
+
17
+ class IncrementalCompression(abc.ABC):
18
+ @abc.abstractmethod
19
+ def compress_incremental(self) -> BytesSteppedGenerator[None]:
20
+ raise NotImplementedError
21
+
22
+ @abc.abstractmethod
23
+ def decompress_incremental(self) -> BytesSteppedReaderGenerator[None]:
24
+ raise NotImplementedError
@@ -0,0 +1,47 @@
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 brotli
12
+ else:
13
+ brotli = lang.proxy_import('brotli')
14
+
15
+
16
+ ##
17
+
18
+
19
+ @dc.dataclass(frozen=True, kw_only=True)
20
+ class BrotliCompression(Compression):
21
+ mode: int | None = None
22
+ quality: int | None = None
23
+ lgwin: int | None = None
24
+ lgblock: int | None = None
25
+
26
+ def compress(self, d: bytes) -> bytes:
27
+ return brotli.compress(
28
+ d,
29
+ **(dict(mode=self.mode) if self.mode is not None else {}),
30
+ **(dict(mode=self.quality) if self.quality is not None else {}),
31
+ **(dict(mode=self.lgwin) if self.lgwin is not None else {}),
32
+ **(dict(mode=self.lgblock) if self.lgblock is not None else {}),
33
+ )
34
+
35
+ def decompress(self, d: bytes) -> bytes:
36
+ return brotli.decompress(
37
+ d,
38
+ )
39
+
40
+
41
+ ##
42
+
43
+
44
+ BROTLI_CODEC = make_compression_codec('brotli', BrotliCompression)
45
+
46
+ # @omlish-manifest
47
+ _BROTLI_LAZY_CODEC = make_compression_lazy_loaded_codec(__name__, 'BROTLI_CODEC', BROTLI_CODEC)