omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (226) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/__about__.py +15 -15
  3. omlish/argparse/all.py +17 -9
  4. omlish/argparse/cli.py +16 -3
  5. omlish/argparse/utils.py +21 -0
  6. omlish/asyncs/asyncio/rlock.py +110 -0
  7. omlish/asyncs/asyncio/sync.py +43 -0
  8. omlish/asyncs/asyncio/utils.py +2 -0
  9. omlish/asyncs/sync.py +25 -0
  10. omlish/bootstrap/_marshal.py +1 -1
  11. omlish/bootstrap/diag.py +12 -21
  12. omlish/bootstrap/main.py +2 -5
  13. omlish/bootstrap/sys.py +27 -28
  14. omlish/cexts/__init__.py +0 -0
  15. omlish/cexts/include/omlish/omlish.hh +1 -0
  16. omlish/collections/__init__.py +13 -1
  17. omlish/collections/attrregistry.py +210 -0
  18. omlish/collections/cache/impl.py +1 -0
  19. omlish/collections/identity.py +1 -0
  20. omlish/collections/mappings.py +28 -0
  21. omlish/collections/trie.py +5 -1
  22. omlish/collections/utils.py +77 -0
  23. omlish/concurrent/all.py +2 -1
  24. omlish/concurrent/futures.py +25 -0
  25. omlish/concurrent/threadlets.py +1 -1
  26. omlish/daemons/reparent.py +2 -3
  27. omlish/daemons/spawning.py +2 -3
  28. omlish/dataclasses/__init__.py +2 -0
  29. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  30. omlish/dataclasses/impl/api/classes/make.py +3 -0
  31. omlish/dataclasses/impl/concerns/repr.py +15 -2
  32. omlish/dataclasses/impl/configs.py +97 -37
  33. omlish/dataclasses/impl/generation/compilation.py +21 -19
  34. omlish/dataclasses/impl/generation/globals.py +1 -0
  35. omlish/dataclasses/impl/generation/ops.py +1 -0
  36. omlish/dataclasses/impl/generation/processor.py +105 -24
  37. omlish/dataclasses/impl/processing/base.py +8 -0
  38. omlish/dataclasses/impl/processing/driving.py +8 -8
  39. omlish/dataclasses/specs.py +1 -0
  40. omlish/dataclasses/tools/modifiers.py +5 -0
  41. omlish/diag/cmds/__init__.py +0 -0
  42. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  43. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  44. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  45. omlish/diag/pycharm.py +16 -2
  46. omlish/diag/pydevd.py +58 -40
  47. omlish/diag/replserver/console.py +1 -1
  48. omlish/dispatch/__init__.py +18 -12
  49. omlish/dispatch/methods.py +50 -140
  50. omlish/dom/rendering.py +1 -1
  51. omlish/formats/dotenv.py +1 -1
  52. omlish/formats/json/stream/__init__.py +13 -0
  53. omlish/funcs/guard.py +225 -0
  54. omlish/graphs/dot/rendering.py +1 -1
  55. omlish/http/all.py +44 -4
  56. omlish/http/clients/asyncs.py +33 -27
  57. omlish/http/clients/base.py +17 -1
  58. omlish/http/clients/coro/__init__.py +0 -0
  59. omlish/http/clients/coro/sync.py +171 -0
  60. omlish/http/clients/default.py +208 -29
  61. omlish/http/clients/executor.py +56 -0
  62. omlish/http/clients/httpx.py +72 -11
  63. omlish/http/clients/middleware.py +181 -0
  64. omlish/http/clients/sync.py +33 -27
  65. omlish/http/clients/syncasync.py +49 -0
  66. omlish/http/clients/urllib.py +6 -3
  67. omlish/http/coro/client/connection.py +15 -6
  68. omlish/http/coro/io.py +2 -0
  69. omlish/http/flasky/__init__.py +40 -0
  70. omlish/http/flasky/_compat.py +2 -0
  71. omlish/http/flasky/api.py +82 -0
  72. omlish/http/flasky/app.py +203 -0
  73. omlish/http/flasky/cvs.py +59 -0
  74. omlish/http/flasky/requests.py +20 -0
  75. omlish/http/flasky/responses.py +23 -0
  76. omlish/http/flasky/routes.py +23 -0
  77. omlish/http/flasky/types.py +15 -0
  78. omlish/http/urls.py +67 -0
  79. omlish/inject/__init__.py +38 -18
  80. omlish/inject/_dataclasses.py +4986 -0
  81. omlish/inject/binder.py +4 -48
  82. omlish/inject/elements.py +27 -0
  83. omlish/inject/helpers/__init__.py +0 -0
  84. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  85. omlish/inject/{tags.py → helpers/id.py} +2 -2
  86. omlish/inject/helpers/multis.py +143 -0
  87. omlish/inject/helpers/wrappers.py +54 -0
  88. omlish/inject/impl/elements.py +47 -17
  89. omlish/inject/impl/injector.py +20 -19
  90. omlish/inject/impl/inspect.py +10 -1
  91. omlish/inject/impl/maysync.py +3 -4
  92. omlish/inject/impl/multis.py +3 -0
  93. omlish/inject/impl/sync.py +3 -4
  94. omlish/inject/injector.py +31 -2
  95. omlish/inject/inspect.py +35 -0
  96. omlish/inject/maysync.py +2 -4
  97. omlish/inject/multis.py +8 -0
  98. omlish/inject/overrides.py +3 -3
  99. omlish/inject/privates.py +6 -0
  100. omlish/inject/providers.py +3 -2
  101. omlish/inject/sync.py +5 -4
  102. omlish/io/buffers.py +118 -0
  103. omlish/io/readers.py +29 -0
  104. omlish/iterators/transforms.py +2 -2
  105. omlish/lang/__init__.py +178 -97
  106. omlish/lang/_asyncs.cc +186 -0
  107. omlish/lang/asyncs.py +17 -0
  108. omlish/lang/casing.py +11 -0
  109. omlish/lang/contextmanagers.py +28 -4
  110. omlish/lang/functions.py +31 -22
  111. omlish/lang/imports/_capture.cc +11 -9
  112. omlish/lang/imports/capture.py +363 -170
  113. omlish/lang/imports/proxy.py +455 -152
  114. omlish/lang/lazyglobals.py +22 -9
  115. omlish/lang/params.py +17 -0
  116. omlish/lang/recursion.py +0 -1
  117. omlish/lang/sequences.py +124 -0
  118. omlish/lite/abstract.py +54 -24
  119. omlish/lite/asyncs.py +2 -2
  120. omlish/lite/attrops.py +2 -0
  121. omlish/lite/contextmanagers.py +4 -4
  122. omlish/lite/dataclasses.py +44 -0
  123. omlish/lite/maybes.py +8 -0
  124. omlish/lite/maysync.py +1 -5
  125. omlish/lite/pycharm.py +1 -1
  126. omlish/lite/typing.py +6 -0
  127. omlish/logs/all.py +1 -1
  128. omlish/logs/utils.py +1 -1
  129. omlish/manifests/loading.py +2 -1
  130. omlish/marshal/__init__.py +33 -13
  131. omlish/marshal/_dataclasses.py +2774 -0
  132. omlish/marshal/base/configs.py +12 -0
  133. omlish/marshal/base/contexts.py +36 -21
  134. omlish/marshal/base/funcs.py +8 -11
  135. omlish/marshal/base/options.py +8 -0
  136. omlish/marshal/base/registries.py +146 -44
  137. omlish/marshal/base/types.py +40 -16
  138. omlish/marshal/composite/iterables.py +33 -20
  139. omlish/marshal/composite/literals.py +20 -18
  140. omlish/marshal/composite/mappings.py +36 -23
  141. omlish/marshal/composite/maybes.py +29 -19
  142. omlish/marshal/composite/newtypes.py +16 -16
  143. omlish/marshal/composite/optionals.py +14 -14
  144. omlish/marshal/composite/special.py +15 -15
  145. omlish/marshal/composite/unions/__init__.py +0 -0
  146. omlish/marshal/composite/unions/literals.py +93 -0
  147. omlish/marshal/composite/unions/primitives.py +103 -0
  148. omlish/marshal/factories/invalidate.py +18 -68
  149. omlish/marshal/factories/method.py +26 -0
  150. omlish/marshal/factories/moduleimport/factories.py +22 -65
  151. omlish/marshal/factories/multi.py +13 -25
  152. omlish/marshal/factories/recursive.py +42 -56
  153. omlish/marshal/factories/typecache.py +29 -74
  154. omlish/marshal/factories/typemap.py +42 -43
  155. omlish/marshal/objects/dataclasses.py +129 -106
  156. omlish/marshal/objects/marshal.py +18 -14
  157. omlish/marshal/objects/namedtuples.py +48 -42
  158. omlish/marshal/objects/unmarshal.py +19 -15
  159. omlish/marshal/polymorphism/marshal.py +9 -11
  160. omlish/marshal/polymorphism/metadata.py +16 -5
  161. omlish/marshal/polymorphism/standard.py +13 -1
  162. omlish/marshal/polymorphism/unions.py +15 -105
  163. omlish/marshal/polymorphism/unmarshal.py +9 -10
  164. omlish/marshal/singular/enums.py +14 -18
  165. omlish/marshal/standard.py +10 -6
  166. omlish/marshal/trivial/any.py +1 -1
  167. omlish/marshal/trivial/forbidden.py +21 -26
  168. omlish/metadata.py +23 -1
  169. omlish/os/forkhooks.py +4 -0
  170. omlish/os/pidfiles/pinning.py +2 -2
  171. omlish/reflect/types.py +1 -0
  172. omlish/secrets/marshal.py +1 -1
  173. omlish/specs/jmespath/__init__.py +12 -3
  174. omlish/specs/jmespath/_dataclasses.py +2893 -0
  175. omlish/specs/jmespath/ast.py +1 -1
  176. omlish/specs/jsonrpc/__init__.py +13 -0
  177. omlish/specs/jsonrpc/_marshal.py +32 -23
  178. omlish/specs/jsonrpc/conns.py +10 -7
  179. omlish/specs/jsonschema/_marshal.py +1 -1
  180. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  181. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  182. omlish/specs/openapi/_marshal.py +31 -22
  183. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  184. omlish/sql/queries/_marshal.py +2 -2
  185. omlish/sql/queries/rendering.py +1 -1
  186. omlish/sql/tabledefs/_marshal.py +1 -1
  187. omlish/subprocesses/base.py +4 -0
  188. omlish/subprocesses/editor.py +1 -1
  189. omlish/sync.py +155 -21
  190. omlish/term/alt.py +60 -0
  191. omlish/term/confirm.py +8 -8
  192. omlish/term/pager.py +235 -0
  193. omlish/term/terminfo.py +935 -0
  194. omlish/term/termstate.py +67 -0
  195. omlish/term/vt100/terminal.py +0 -3
  196. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  197. omlish/testing/pytest/plugins/skips.py +2 -1
  198. omlish/testing/unittest/main.py +3 -3
  199. omlish/text/docwrap/__init__.py +3 -0
  200. omlish/text/docwrap/__main__.py +11 -0
  201. omlish/text/docwrap/api.py +83 -0
  202. omlish/text/docwrap/cli.py +86 -0
  203. omlish/text/docwrap/groups.py +84 -0
  204. omlish/text/docwrap/lists.py +167 -0
  205. omlish/text/docwrap/parts.py +146 -0
  206. omlish/text/docwrap/reflowing.py +106 -0
  207. omlish/text/docwrap/rendering.py +151 -0
  208. omlish/text/docwrap/utils.py +11 -0
  209. omlish/text/docwrap/wrapping.py +59 -0
  210. omlish/text/filecache.py +2 -2
  211. omlish/text/lorem.py +6 -0
  212. omlish/text/parts.py +2 -2
  213. omlish/text/textwrap.py +51 -0
  214. omlish/typedvalues/marshal.py +85 -59
  215. omlish/typedvalues/values.py +2 -1
  216. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
  217. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
  218. omlish/dataclasses/impl/generation/mangling.py +0 -18
  219. omlish/funcs/match.py +0 -227
  220. omlish/marshal/factories/match.py +0 -34
  221. omlish/marshal/factories/simple.py +0 -28
  222. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  223. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
  224. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
  225. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
  226. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
omlish/inject/injector.py CHANGED
@@ -1,9 +1,12 @@
1
1
  import abc
2
2
  import typing as ta
3
3
 
4
+ from .. import check
4
5
  from .. import lang
6
+ from .elements import CollectedElements
5
7
  from .elements import Elemental
6
8
  from .elements import as_elements
9
+ from .elements import collect_elements
7
10
  from .inspect import KwargsTarget
8
11
  from .keys import Key
9
12
 
@@ -44,5 +47,31 @@ class AsyncInjector(lang.Abstract):
44
47
  return self.provide(target)
45
48
 
46
49
 
47
- def create_async_injector(*args: Elemental) -> ta.Awaitable[AsyncInjector]:
48
- return _injector.create_async_injector(as_elements(*args))
50
+ ##
51
+
52
+
53
+ @ta.final
54
+ class _InjectorCreator(ta.Generic[T]):
55
+ def __init__(self, fac: ta.Callable[[CollectedElements], T]) -> None:
56
+ self._fac = fac
57
+
58
+ @ta.overload
59
+ def __call__(self, es: CollectedElements, /) -> T: ...
60
+
61
+ @ta.overload
62
+ def __call__(self, *es: Elemental) -> T: ...
63
+
64
+ def __call__(self, arg0, *argv):
65
+ ce: CollectedElements
66
+ if isinstance(arg0, CollectedElements):
67
+ check.arg(not argv)
68
+ ce = arg0
69
+ else:
70
+ ce = collect_elements(as_elements(arg0, *argv))
71
+ return self._fac(ce)
72
+
73
+
74
+ ##
75
+
76
+
77
+ create_async_injector = _InjectorCreator[ta.Awaitable[AsyncInjector]](lambda ce: _injector.create_async_injector(ce))
omlish/inject/inspect.py CHANGED
@@ -2,6 +2,7 @@ import typing as ta
2
2
 
3
3
  from .. import lang
4
4
  from .keys import Key
5
+ from .keys import as_key
5
6
 
6
7
 
7
8
  if ta.TYPE_CHECKING:
@@ -21,11 +22,45 @@ class Kwarg(ta.NamedTuple):
21
22
  key: Key
22
23
  has_default: bool
23
24
 
25
+ @classmethod
26
+ def of(
27
+ cls,
28
+ name: str,
29
+ key: Key,
30
+ *,
31
+ has_default: bool = False,
32
+ ) -> 'Kwarg':
33
+ return cls(
34
+ name,
35
+ key,
36
+ has_default,
37
+ )
38
+
24
39
 
25
40
  class KwargsTarget(ta.NamedTuple):
26
41
  obj: ta.Any
27
42
  kwargs: ta.Sequence[Kwarg]
28
43
 
44
+ @classmethod
45
+ def of(
46
+ cls,
47
+ obj: ta.Any,
48
+ *kws: Kwarg,
49
+ **kwargs: tuple[Key, bool] | Key | ta.Any,
50
+ ) -> 'KwargsTarget':
51
+ kw_kwargs: list[Kwarg] = []
52
+ for n, v in kwargs.items():
53
+ if isinstance(v, tuple):
54
+ kw_k, kw_hd = v
55
+ kw_kwargs.append(Kwarg.of(n, kw_k, has_default=kw_hd))
56
+ else:
57
+ kw_kwargs.append(Kwarg.of(n, as_key(v)))
58
+
59
+ return cls(
60
+ obj,
61
+ [*kws, *kw_kwargs],
62
+ )
63
+
29
64
 
30
65
  def tag(obj: T, **kwargs: ta.Any) -> T:
31
66
  return _inspect.tag(obj, **kwargs)
omlish/inject/maysync.py CHANGED
@@ -1,8 +1,7 @@
1
1
  import typing as ta
2
2
 
3
3
  from .. import lang
4
- from .elements import Elemental
5
- from .elements import as_elements
4
+ from .injector import _InjectorCreator
6
5
  from .sync import Injector
7
6
 
8
7
 
@@ -25,5 +24,4 @@ class MaysyncInjector(Injector, lang.Abstract):
25
24
  ##
26
25
 
27
26
 
28
- def create_maysync_injector(*args: Elemental) -> MaysyncInjector:
29
- return _maysync.create_maysync_injector(as_elements(*args))
27
+ create_maysync_injector = _InjectorCreator[MaysyncInjector](lambda ce: _maysync.create_maysync_injector(ce))
omlish/inject/multis.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """
2
2
  TODO:
3
+ - DynamicSetBinding / DynamicMapBinding ? provider of set[T] / map[K, V] ?
4
+ - doable not guicey - too much dynamism
3
5
  - scopes
4
6
  """
5
7
  import collections.abc
@@ -94,6 +96,9 @@ class SetBinder(ElementGenerator, ta.Generic[T]):
94
96
  yield from self._sbs
95
97
 
96
98
 
99
+ set_binder = SetBinder
100
+
101
+
97
102
  #
98
103
 
99
104
 
@@ -123,3 +128,6 @@ class MapBinder(ElementGenerator, ta.Generic[K, V]):
123
128
  def __iter__(self) -> ta.Iterator[Element]:
124
129
  yield self._map_provider_binding
125
130
  yield from self._mbs
131
+
132
+
133
+ map_binder = MapBinder
@@ -14,9 +14,9 @@ from .elements import as_elements
14
14
  @dc.dataclass(frozen=True)
15
15
  @dc.extra_class_params(cache_hash=True)
16
16
  class Overrides(Element, lang.Final):
17
- ovr: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
18
17
  src: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
18
+ ovr: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
19
19
 
20
20
 
21
- def override(ovr: ta.Any, *a: ta.Any) -> Element:
22
- return Overrides(as_elements(ovr), as_elements(*a))
21
+ def override(src: ta.Any, *ovr: ta.Any) -> Element:
22
+ return Overrides(as_elements(src), as_elements(*ovr))
omlish/inject/privates.py CHANGED
@@ -18,6 +18,12 @@ class Expose(Element, lang.Final):
18
18
  key: Key = dc.xfield(coerce=as_key)
19
19
 
20
20
 
21
+ expose = Expose
22
+
23
+
24
+ #
25
+
26
+
21
27
  @dc.dataclass(frozen=True)
22
28
  @dc.extra_class_params(cache_hash=True)
23
29
  class Private(Element, lang.Final):
@@ -3,6 +3,7 @@ import typing as ta
3
3
  from .. import check
4
4
  from .. import dataclasses as dc
5
5
  from .. import lang
6
+ from .inspect import KwargsTarget
6
7
  from .keys import Key
7
8
 
8
9
 
@@ -20,13 +21,13 @@ class Provider(lang.Abstract):
20
21
  @dc.dataclass(frozen=True)
21
22
  @dc.extra_class_params(cache_hash=True)
22
23
  class AsyncFnProvider(Provider):
23
- fn: ta.Any = dc.xfield(validate=callable)
24
+ fn: ta.Any = dc.xfield(validate=lambda v: callable(v) or isinstance(v, KwargsTarget))
24
25
 
25
26
 
26
27
  @dc.dataclass(frozen=True)
27
28
  @dc.extra_class_params(cache_hash=True)
28
29
  class FnProvider(Provider):
29
- fn: ta.Any = dc.xfield(validate=callable)
30
+ fn: ta.Any = dc.xfield(validate=lambda v: callable(v) or isinstance(v, KwargsTarget))
30
31
 
31
32
 
32
33
  @dc.dataclass(frozen=True)
omlish/inject/sync.py CHANGED
@@ -2,8 +2,7 @@ import abc
2
2
  import typing as ta
3
3
 
4
4
  from .. import lang
5
- from .elements import Elemental
6
- from .elements import as_elements
5
+ from .injector import _InjectorCreator
7
6
  from .inspect import KwargsTarget
8
7
  from .keys import Key
9
8
 
@@ -44,5 +43,7 @@ class Injector(lang.Abstract):
44
43
  return self.provide(target)
45
44
 
46
45
 
47
- def create_injector(*args: Elemental) -> Injector:
48
- return _sync.create_injector(as_elements(*args))
46
+ ##
47
+
48
+
49
+ create_injector = _InjectorCreator[Injector](lambda ce: _sync.create_injector(ce))
omlish/io/buffers.py CHANGED
@@ -1,10 +1,18 @@
1
1
  # ruff: noqa: UP006 UP007 UP043 UP045
2
2
  # @omlish-lite
3
+ """
4
+ TODO:
5
+ - overhaul and just coro-ify pyio?
6
+ """
3
7
  import io
4
8
  import typing as ta
5
9
 
6
10
  from ..lite.attrops import attr_repr
7
11
  from ..lite.check import check
12
+ from .readers import AsyncBufferedBytesReader
13
+ from .readers import AsyncRawBytesReader
14
+ from .readers import BufferedBytesReader
15
+ from .readers import RawBytesReader
8
16
 
9
17
 
10
18
  ##
@@ -183,6 +191,9 @@ class ReadableListBuffer:
183
191
 
184
192
  self._lst: list[bytes] = []
185
193
 
194
+ def __bool__(self) -> ta.NoReturn:
195
+ raise TypeError("Use 'buf is not None' or 'len(buf)'.")
196
+
186
197
  def __len__(self) -> int:
187
198
  return sum(map(len, self._lst))
188
199
 
@@ -208,6 +219,9 @@ class ReadableListBuffer:
208
219
 
209
220
  def read(self, n: ta.Optional[int] = None) -> ta.Optional[bytes]:
210
221
  if n is None:
222
+ if not self._lst:
223
+ return b''
224
+
211
225
  o = b''.join(self._lst)
212
226
  self._lst = []
213
227
  return o
@@ -246,6 +260,110 @@ class ReadableListBuffer:
246
260
  r = self.read_until_(delim)
247
261
  return r if isinstance(r, bytes) else None
248
262
 
263
+ #
264
+
265
+ DEFAULT_BUFFERED_READER_CHUNK_SIZE: ta.ClassVar[int] = -1
266
+
267
+ @ta.final
268
+ class _BufferedBytesReader(BufferedBytesReader):
269
+ def __init__(
270
+ self,
271
+ raw: RawBytesReader,
272
+ buf: 'ReadableListBuffer',
273
+ *,
274
+ chunk_size: ta.Optional[int] = None,
275
+ ) -> None:
276
+ self._raw = raw
277
+ self._buf = buf
278
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
279
+
280
+ def read1(self, n: int = -1, /) -> bytes:
281
+ if n < 0:
282
+ n = self._chunk_size
283
+ if not n:
284
+ return b''
285
+ if 0 < n <= len(self._buf):
286
+ return self._buf.read(n) or b''
287
+ return self._raw.read1(n)
288
+
289
+ def read(self, /, n: int = -1) -> bytes:
290
+ if n < 0:
291
+ return self.readall()
292
+ while len(self._buf) < n:
293
+ if not (b := self._raw.read1(n)):
294
+ break
295
+ self._buf.feed(b)
296
+ return self._buf.read(n) or b''
297
+
298
+ def readall(self) -> bytes:
299
+ buf = io.BytesIO()
300
+ buf.write(self._buf.read() or b'')
301
+ while (b := self._raw.read1(self._chunk_size)):
302
+ buf.write(b)
303
+ return buf.getvalue()
304
+
305
+ def new_buffered_reader(
306
+ self,
307
+ raw: RawBytesReader,
308
+ *,
309
+ chunk_size: ta.Optional[int] = None,
310
+ ) -> BufferedBytesReader:
311
+ return self._BufferedBytesReader(
312
+ raw,
313
+ self,
314
+ chunk_size=chunk_size,
315
+ )
316
+
317
+ @ta.final
318
+ class _AsyncBufferedBytesReader(AsyncBufferedBytesReader):
319
+ def __init__(
320
+ self,
321
+ raw: AsyncRawBytesReader,
322
+ buf: 'ReadableListBuffer',
323
+ *,
324
+ chunk_size: ta.Optional[int] = None,
325
+ ) -> None:
326
+ self._raw = raw
327
+ self._buf = buf
328
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
329
+
330
+ async def read1(self, n: int = -1, /) -> bytes:
331
+ if n < 0:
332
+ n = self._chunk_size
333
+ if not n:
334
+ return b''
335
+ if 0 < n <= len(self._buf):
336
+ return self._buf.read(n) or b''
337
+ return await self._raw.read1(n)
338
+
339
+ async def read(self, /, n: int = -1) -> bytes:
340
+ if n < 0:
341
+ return await self.readall()
342
+ while len(self._buf) < n:
343
+ if not (b := await self._raw.read1(n)):
344
+ break
345
+ self._buf.feed(b)
346
+ return self._buf.read(n) or b''
347
+
348
+ async def readall(self) -> bytes:
349
+ buf = io.BytesIO()
350
+ buf.write(self._buf.read() or b'')
351
+ while b := await self._raw.read1(self._chunk_size):
352
+ buf.write(b)
353
+ return buf.getvalue()
354
+
355
+ def new_async_buffered_reader(
356
+ self,
357
+ raw: AsyncRawBytesReader,
358
+ *,
359
+ chunk_size: ta.Optional[int] = None,
360
+ ) -> AsyncBufferedBytesReader:
361
+ return self._AsyncBufferedBytesReader(
362
+ raw,
363
+ self,
364
+ chunk_size=chunk_size,
365
+ )
366
+
249
367
 
250
368
  ##
251
369
 
omlish/io/readers.py ADDED
@@ -0,0 +1,29 @@
1
+ # ruff: noqa: UP045
2
+ # @omlish-lite
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ class RawBytesReader(ta.Protocol):
10
+ def read1(self, n: int = -1, /) -> bytes: ...
11
+
12
+
13
+ class BufferedBytesReader(RawBytesReader, ta.Protocol):
14
+ def read(self, n: int = -1, /) -> bytes: ...
15
+
16
+ def readall(self) -> bytes: ...
17
+
18
+
19
+ #
20
+
21
+
22
+ class AsyncRawBytesReader(ta.Protocol):
23
+ def read1(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
24
+
25
+
26
+ class AsyncBufferedBytesReader(AsyncRawBytesReader, ta.Protocol):
27
+ def read(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
28
+
29
+ def readall(self) -> ta.Awaitable[bytes]: ...
@@ -83,8 +83,8 @@ apply = Apply
83
83
 
84
84
  @dc.dataclass(frozen=True)
85
85
  class Flatten(Transform_[ta.Iterable[T], T]):
86
- def __call__(self, it: ta.Iterable[ta.Iterable[T]]) -> ta.Iterable[U]:
87
- return itertools.chain.from_iterable(it) # type: ignore[arg-type]
86
+ def __call__(self, it: ta.Iterable[ta.Iterable[T]]) -> ta.Iterable[T]:
87
+ return itertools.chain.from_iterable(it)
88
88
 
89
89
 
90
90
  flatten = Flatten