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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. omlish/__about__.py +4 -2
  2. omlish/check.py +2 -0
  3. omlish/collections/coerce.py +1 -0
  4. omlish/docker/__init__.py +2 -1
  5. omlish/docker/helpers.py +0 -6
  6. omlish/funcs/pairs.py +2 -2
  7. omlish/io/compress/__init__.py +9 -0
  8. omlish/io/compress/abc.py +4 -4
  9. omlish/io/compress/adapters.py +12 -11
  10. omlish/io/compress/base.py +24 -0
  11. omlish/io/compress/brotli.py +33 -0
  12. omlish/io/compress/bz2.py +26 -21
  13. omlish/io/compress/gzip.py +43 -10
  14. omlish/io/compress/lz4.py +77 -0
  15. omlish/io/compress/lzma.py +51 -16
  16. omlish/io/compress/snappy.py +20 -0
  17. omlish/io/compress/zlib.py +60 -0
  18. omlish/io/compress/zstd.py +30 -0
  19. omlish/io/generators/__init__.py +53 -0
  20. omlish/io/generators/consts.py +1 -0
  21. omlish/io/generators/direct.py +13 -0
  22. omlish/io/generators/readers.py +15 -9
  23. omlish/io/generators/stepped.py +85 -14
  24. omlish/io/pyio.py +5 -2
  25. omlish/lang/__init__.py +1 -0
  26. omlish/lang/iterables.py +20 -0
  27. omlish/lite/docker.py +3 -0
  28. omlish/lite/marshal.py +213 -141
  29. omlish/lite/pycharm.py +48 -0
  30. omlish/lite/subprocesses.py +13 -0
  31. omlish/logs/abc.py +0 -1
  32. {omlish-0.0.0.dev141.dist-info → omlish-0.0.0.dev143.dist-info}/METADATA +3 -1
  33. {omlish-0.0.0.dev141.dist-info → omlish-0.0.0.dev143.dist-info}/RECORD +37 -29
  34. omlish/io/compress/types.py +0 -29
  35. {omlish-0.0.0.dev141.dist-info → omlish-0.0.0.dev143.dist-info}/LICENSE +0 -0
  36. {omlish-0.0.0.dev141.dist-info → omlish-0.0.0.dev143.dist-info}/WHEEL +0 -0
  37. {omlish-0.0.0.dev141.dist-info → omlish-0.0.0.dev143.dist-info}/entry_points.txt +0 -0
  38. {omlish-0.0.0.dev141.dist-info → omlish-0.0.0.dev143.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,30 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from ... import lang
5
+ from .base import Compression
6
+
7
+
8
+ if ta.TYPE_CHECKING:
9
+ import zstandard
10
+ else:
11
+ zstandard = lang.proxy_import('zstandard')
12
+
13
+
14
+ @dc.dataclass(frozen=True, kw_only=True)
15
+ class ZstdCompression(Compression):
16
+ level: int | None = None
17
+
18
+ max_output_size: int = 0
19
+
20
+ def compress(self, d: bytes) -> bytes:
21
+ return zstandard.compress(
22
+ d,
23
+ **(dict(level=self.level) if self.level is not None else {}),
24
+ )
25
+
26
+ def decompress(self, d: bytes) -> bytes:
27
+ return zstandard.decompress(
28
+ d,
29
+ max_output_size=self.max_output_size,
30
+ )
@@ -0,0 +1,53 @@
1
+ from .consts import ( # noqa
2
+ DEFAULT_BUFFER_SIZE,
3
+ )
4
+
5
+ from .direct import ( # noqa
6
+ DirectGenerator,
7
+
8
+ BytesDirectGenerator,
9
+ StrDirectGenerator,
10
+ )
11
+
12
+ from .readers import ( # noqa
13
+ ReaderGenerator,
14
+ BytesReaderGenerator,
15
+ StrReaderGenerator,
16
+
17
+ ExactReaderGenerator,
18
+ BytesExactReaderGenerator,
19
+ StrExactReaderGenerator,
20
+
21
+ GeneratorReader,
22
+
23
+ PrependableGeneratorReader,
24
+ PrependableBytesGeneratorReader,
25
+ PrependableStrGeneratorReader,
26
+ prependable_bytes_generator_reader,
27
+ prependable_str_generator_reader,
28
+
29
+ BufferedGeneratorReader,
30
+ BufferedBytesGeneratorReader,
31
+ BufferedStrGeneratorReader,
32
+ buffered_bytes_generator_reader,
33
+ buffered_str_generator_reader,
34
+ )
35
+
36
+ from .stepped import ( # noqa
37
+ SteppedGenerator,
38
+ BytesSteppedGenerator,
39
+ StrSteppedGenerator,
40
+ BytesToStrSteppedGenerator,
41
+ StrToBytesSteppedGenerator,
42
+
43
+ SteppedReaderGenerator,
44
+ BytesSteppedReaderGenerator,
45
+ StrSteppedReaderGenerator,
46
+
47
+ flatmap_stepped_generator,
48
+
49
+ joined_bytes_stepped_generator,
50
+ joined_str_stepped_generator,
51
+
52
+ read_into_bytes_stepped_generator,
53
+ )
@@ -0,0 +1 @@
1
+ DEFAULT_BUFFER_SIZE = 4 * 0x1000
@@ -0,0 +1,13 @@
1
+ import typing as ta
2
+
3
+
4
+ O = ta.TypeVar('O')
5
+ I = ta.TypeVar('I')
6
+ R = ta.TypeVar('R')
7
+
8
+
9
+ # Direct generators yield outputs 1:1 with inputs.
10
+ DirectGenerator: ta.TypeAlias = ta.Generator[O, I, R]
11
+
12
+ BytesDirectGenerator: ta.TypeAlias = DirectGenerator[bytes, bytes, R]
13
+ StrDirectGenerator: ta.TypeAlias = DirectGenerator[str, str, R]
@@ -8,21 +8,29 @@ import abc
8
8
  import typing as ta
9
9
 
10
10
  from ... import check
11
+ from .consts import DEFAULT_BUFFER_SIZE
11
12
 
12
13
 
13
14
  T = ta.TypeVar('T')
15
+
16
+ O = ta.TypeVar('O')
14
17
  I = ta.TypeVar('I')
15
18
  R = ta.TypeVar('R')
19
+
16
20
  AnyT = ta.TypeVar('AnyT', bound=ta.Any)
17
21
 
18
22
 
23
+ # Reader generators yield ints of amounts of data needed, or None for needing any amount of data.
19
24
  ReaderGenerator: ta.TypeAlias = ta.Generator[int | None, I, R]
20
- ExactReaderGenerator: ta.TypeAlias = ta.Generator[int, I, R]
21
25
 
22
26
  BytesReaderGenerator: ta.TypeAlias = ReaderGenerator[bytes, R]
23
- BytesExactReaderGenerator: ta.TypeAlias = ExactReaderGenerator[bytes, R]
24
-
25
27
  StrReaderGenerator: ta.TypeAlias = ReaderGenerator[str, R]
28
+
29
+
30
+ # Exact reader generators always specify read sizes.
31
+ ExactReaderGenerator: ta.TypeAlias = ta.Generator[int, I, R]
32
+
33
+ BytesExactReaderGenerator: ta.TypeAlias = ExactReaderGenerator[bytes, R]
26
34
  StrExactReaderGenerator: ta.TypeAlias = ExactReaderGenerator[str, R]
27
35
 
28
36
 
@@ -44,10 +52,10 @@ class _StrJoiner:
44
52
 
45
53
  class GeneratorReader(abc.ABC, ta.Generic[T]):
46
54
  @abc.abstractmethod
47
- def read(self, sz: int | None) -> ta.Generator[int | None, T, T]:
55
+ def read(self, sz: int | None) -> ReaderGenerator[T, T]:
48
56
  raise NotImplementedError
49
57
 
50
- def read_exact(self, sz: int) -> ta.Generator[int | None, T, T]:
58
+ def read_exact(self, sz: int) -> ReaderGenerator[T, T]:
51
59
  d: ta.Any = yield from self.read(sz)
52
60
  if len(d) != sz:
53
61
  raise EOFError(f'GeneratorReader got {len(d)}, expected {sz}')
@@ -67,7 +75,7 @@ class PrependableGeneratorReader(GeneratorReader[AnyT]):
67
75
  def _join(self, lst: list[AnyT]) -> AnyT:
68
76
  raise NotImplementedError
69
77
 
70
- def read(self, sz: int | None) -> ta.Generator[int | None, AnyT, AnyT]:
78
+ def read(self, sz: int | None) -> ReaderGenerator[AnyT, AnyT]:
71
79
  if not self._queue:
72
80
  d: AnyT = check.not_none((yield sz))
73
81
  return d
@@ -126,8 +134,6 @@ prependable_str_generator_reader = PrependableStrGeneratorReader
126
134
 
127
135
 
128
136
  class BufferedGeneratorReader(PrependableGeneratorReader[AnyT], abc.ABC):
129
- DEFAULT_BUFFER_SIZE = 4 * 0x1000
130
-
131
137
  def __init__(
132
138
  self,
133
139
  buffer_size: int = DEFAULT_BUFFER_SIZE,
@@ -138,7 +144,7 @@ class BufferedGeneratorReader(PrependableGeneratorReader[AnyT], abc.ABC):
138
144
 
139
145
  self._buffer_size = buffer_size
140
146
 
141
- def read(self, sz: int | None) -> ta.Generator[int | None, AnyT, AnyT]:
147
+ def read(self, sz: int | None) -> ReaderGenerator[AnyT, AnyT]:
142
148
  g = super().read(sz)
143
149
  i: ta.Any = None
144
150
  while True:
@@ -1,18 +1,40 @@
1
1
  import typing as ta
2
2
 
3
+ from ... import check
3
4
  from ... import lang
5
+ from .consts import DEFAULT_BUFFER_SIZE
6
+ from .direct import BytesDirectGenerator
7
+ from .direct import StrDirectGenerator
4
8
 
5
9
 
6
10
  T = ta.TypeVar('T')
7
- I = ta.TypeVar('I')
11
+
8
12
  O = ta.TypeVar('O')
13
+ I = ta.TypeVar('I')
14
+ R = ta.TypeVar('R')
15
+
9
16
  OF = ta.TypeVar('OF')
10
17
  OT = ta.TypeVar('OT')
11
- R = ta.TypeVar('R')
12
18
 
13
19
 
20
+ # Stepped generators accept a non-None input, then in response yield zero or more non-None outputs, until yielding None
21
+ # to signal they need more input again.
14
22
  SteppedGenerator: ta.TypeAlias = ta.Generator[O | None, I | None, R]
15
23
 
24
+ # Conventionally, these are sent and themselves yield an empty value to signify termination.
25
+ BytesSteppedGenerator: ta.TypeAlias = SteppedGenerator[bytes, bytes, R]
26
+ StrSteppedGenerator: ta.TypeAlias = SteppedGenerator[str, str, R]
27
+
28
+ BytesToStrSteppedGenerator: ta.TypeAlias = SteppedGenerator[str, bytes, R]
29
+ StrToBytesSteppedGenerator: ta.TypeAlias = SteppedGenerator[bytes, str, R]
30
+
31
+
32
+ # Stepped reader generators emit either an int or None to request input, or emit some other kind of output.
33
+ SteppedReaderGenerator: ta.TypeAlias = ta.Generator[int | None | O, I | None, R]
34
+
35
+ BytesSteppedReaderGenerator: ta.TypeAlias = SteppedReaderGenerator[bytes, bytes, R]
36
+ StrSteppedReaderGenerator: ta.TypeAlias = SteppedReaderGenerator[str, str, R]
37
+
16
38
 
17
39
  ##
18
40
 
@@ -25,10 +47,8 @@ def flatmap_stepped_generator(
25
47
  terminate: ta.Callable[[OF], bool] | None = None,
26
48
  ) -> ta.Generator[OT, I, lang.Maybe[R]]:
27
49
  """
28
- Given a 'stepped generator' - a generator which accepts input items and yields zero or more non-None values in
29
- response until it signals it's ready for the next input by yielding None - and a function taking a list, returns a
30
- 1:1 generator which accepts input, builds a list of yielded generator output, calls the given function with that
31
- list, and yields the result.
50
+ Given a stepped generator and a function taking a list, returns a direct (1:1) generator which accepts input, builds
51
+ a list of yielded generator output, calls the given function with that list, and yields the result.
32
52
 
33
53
  An optional terminate function may be provided which will cause this function to return early if it returns true for
34
54
  an encountered yielded value. The encountered value causing termination will be included in the list sent to the
@@ -89,16 +109,67 @@ def _is_empty(o: T) -> bool:
89
109
  return len(o) < 1 # type: ignore
90
110
 
91
111
 
112
+ def joined_bytes_stepped_generator(g: BytesSteppedGenerator[R]) -> BytesDirectGenerator[R]:
113
+ return flatmap_stepped_generator(_join_bytes, g, terminate=_is_empty)
114
+
115
+
116
+ def joined_str_stepped_generator(g: StrSteppedGenerator[R]) -> StrDirectGenerator[R]:
117
+ return flatmap_stepped_generator(_join_str, g, terminate=_is_empty)
118
+
119
+
92
120
  ##
93
121
 
94
122
 
95
- def joined_bytes_stepped_generator(
96
- g: ta.Generator[bytes | None, bytes | None, R],
97
- ) -> ta.Generator[bytes, bytes, R]:
98
- return flatmap_stepped_generator(_join_bytes, g, terminate=_is_empty)
123
+ def read_into_bytes_stepped_generator(
124
+ g: BytesSteppedGenerator,
125
+ f: ta.IO,
126
+ *,
127
+ read_size: int = DEFAULT_BUFFER_SIZE,
128
+ ) -> ta.Iterator[bytes]:
129
+ yield from lang.genmap( # type: ignore[misc]
130
+ joined_bytes_stepped_generator(g),
131
+ lang.readiter(f, read_size),
132
+ )
99
133
 
100
134
 
101
- def joined_str_stepped_generator(
102
- g: ta.Generator[str | None, str | None, R],
103
- ) -> ta.Generator[str, str, R]:
104
- return flatmap_stepped_generator(_join_str, g, terminate=_is_empty)
135
+ def read_into_str_stepped_generator(
136
+ g: StrSteppedGenerator,
137
+ f: ta.TextIO,
138
+ *,
139
+ read_size: int = DEFAULT_BUFFER_SIZE,
140
+ ) -> ta.Iterator[str]:
141
+ yield from lang.genmap(
142
+ joined_str_stepped_generator(g),
143
+ lang.readiter(f, read_size),
144
+ )
145
+
146
+
147
+ ##
148
+
149
+
150
+ @lang.autostart
151
+ def buffer_bytes_stepped_reader_generator(g: BytesSteppedReaderGenerator) -> BytesSteppedGenerator:
152
+ o = g.send(None)
153
+ buf: ta.Any = None
154
+
155
+ while True:
156
+ if not buf:
157
+ buf = check.isinstance((yield None), bytes)
158
+
159
+ if o is None or not buf:
160
+ i = buf
161
+ elif isinstance(o, int):
162
+ if len(buf) < o:
163
+ raise NotImplementedError
164
+ i = buf[:o]
165
+ buf = buf[o:]
166
+ else:
167
+ raise TypeError(o)
168
+
169
+ while True:
170
+ o = g.send(i)
171
+ i = None
172
+ if isinstance(o, bytes):
173
+ check.none((yield o))
174
+ else:
175
+ break
omlish/io/pyio.py CHANGED
@@ -4,7 +4,7 @@
4
4
  """
5
5
  Python implementation of the io module.
6
6
 
7
- https://github.com/python/cpython/blob/8fa4dc4ba8646c59f945f2451c53e2919f066065/Lib/_pyio.py
7
+ https://github.com/python/cpython/blob/8b3cccf3f9508572d85b0044519f2bd5715dacad/Lib/_pyio.py
8
8
  """
9
9
  # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
10
10
  # --------------------------------------------
@@ -2580,8 +2580,11 @@ class TextIOWrapper(TextIOBase):
2580
2580
  decoder = self._decoder or self._get_decoder()
2581
2581
 
2582
2582
  if size < 0:
2583
+ chunk = self.buffer.read()
2584
+ if chunk is None:
2585
+ raise BlockingIOError("Read returned None.")
2583
2586
  # Read everything.
2584
- result = self._get_decoded_chars() + decoder.decode(self.buffer.read(), final=True)
2587
+ result = self._get_decoded_chars() + decoder.decode(chunk, final=True)
2585
2588
  if self._snapshot is not None:
2586
2589
  self._set_decoded_chars('')
2587
2590
  self._snapshot = None
omlish/lang/__init__.py CHANGED
@@ -156,6 +156,7 @@ from .iterables import ( # noqa
156
156
  itergen,
157
157
  peek,
158
158
  prodrange,
159
+ readiter,
159
160
  renumerate,
160
161
  take,
161
162
  )
omlish/lang/iterables.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import dataclasses as dc
2
+ import functools
2
3
  import itertools
3
4
  import typing as ta
4
5
 
@@ -46,6 +47,25 @@ def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterable[T]:
46
47
  yield v
47
48
 
48
49
 
50
+ @ta.overload
51
+ def readiter(f: ta.TextIO, sz: int) -> ta.Iterator[str]:
52
+ ...
53
+
54
+
55
+ @ta.overload
56
+ def readiter(f: ta.BinaryIO, sz: int) -> ta.Iterator[bytes]:
57
+ ...
58
+
59
+
60
+ @ta.overload
61
+ def readiter(f: ta.IO, sz: int) -> ta.Iterator[ta.AnyStr]:
62
+ ...
63
+
64
+
65
+ def readiter(f, sz):
66
+ return iter(functools.partial(f.read, sz), None)
67
+
68
+
49
69
  @dc.dataclass(frozen=True)
50
70
  class IterGen(ta.Generic[T]):
51
71
  fn: ta.Callable[[], ta.Iterable[T]]
omlish/lite/docker.py CHANGED
@@ -2,6 +2,9 @@ import re
2
2
  import sys
3
3
 
4
4
 
5
+ DOCKER_FOR_MAC_HOSTNAME = 'docker.for.mac.localhost'
6
+
7
+
5
8
  _LIKELY_IN_DOCKER_PATTERN = re.compile(r'^overlay / .*/(docker|desktop-containerd)/')
6
9
 
7
10