python-filewrap 0.0.3.1__py3-none-any.whl → 0.0.4__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.
- filewrap/__init__.py +172 -73
- {python_filewrap-0.0.3.1.dist-info → python_filewrap-0.0.4.dist-info}/METADATA +2 -1
- python_filewrap-0.0.4.dist-info/RECORD +7 -0
- python_filewrap-0.0.3.1.dist-info/RECORD +0 -7
- {python_filewrap-0.0.3.1.dist-info → python_filewrap-0.0.4.dist-info}/LICENSE +0 -0
- {python_filewrap-0.0.3.1.dist-info → python_filewrap-0.0.4.dist-info}/WHEEL +0 -0
filewrap/__init__.py
CHANGED
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 0,
|
|
5
|
+
__version__ = (0, 0, 4)
|
|
6
6
|
__all__ = [
|
|
7
|
-
"SupportsRead", "SupportsWrite",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
7
|
+
"SupportsRead", "SupportsWrite",
|
|
8
|
+
"bio_chunk_iter", "bio_chunk_async_iter",
|
|
9
|
+
"bio_skip_iter", "bio_skip_async_iter",
|
|
10
|
+
"bytes_iter_to_reader", "bytes_iter_to_async_reader",
|
|
10
11
|
]
|
|
11
12
|
|
|
12
|
-
from asyncio import to_thread
|
|
13
|
+
from asyncio import to_thread, Lock as AsyncLock
|
|
13
14
|
from collections.abc import Awaitable, AsyncIterable, Iterable
|
|
14
15
|
from functools import update_wrapper
|
|
15
16
|
from inspect import isawaitable, iscoroutinefunction
|
|
16
17
|
from collections.abc import AsyncIterator, Callable, Iterator
|
|
17
18
|
from shutil import COPY_BUFSIZE # type: ignore
|
|
19
|
+
from threading import Lock
|
|
18
20
|
from typing import Any, Protocol, TypeVar
|
|
19
21
|
|
|
22
|
+
from asynctools import ensure_async
|
|
23
|
+
|
|
20
24
|
|
|
21
25
|
_T_co = TypeVar("_T_co", covariant=True)
|
|
22
26
|
_T_contra = TypeVar("_T_contra", contravariant=True)
|
|
@@ -30,17 +34,6 @@ class SupportsWrite(Protocol[_T_contra]):
|
|
|
30
34
|
def write(self, __s: _T_contra) -> object: ...
|
|
31
35
|
|
|
32
36
|
|
|
33
|
-
def ensure_async(func, /):
|
|
34
|
-
if iscoroutinefunction(func):
|
|
35
|
-
return func
|
|
36
|
-
async def wrapper(*args, **kwds):
|
|
37
|
-
ret = to_thread(func, *args, **kwds)
|
|
38
|
-
if isawaitable(ret):
|
|
39
|
-
ret = await ret
|
|
40
|
-
return ret
|
|
41
|
-
return update_wrapper(wrapper, func)
|
|
42
|
-
|
|
43
|
-
|
|
44
37
|
def bio_chunk_iter(
|
|
45
38
|
bio: SupportsRead[bytes] | Callable[[int], bytes],
|
|
46
39
|
/,
|
|
@@ -242,76 +235,182 @@ async def bio_skip_async_iter(
|
|
|
242
235
|
yield length
|
|
243
236
|
|
|
244
237
|
|
|
245
|
-
def
|
|
246
|
-
|
|
238
|
+
def bytes_iter_to_reader(
|
|
239
|
+
it: Iterable[bytes | bytearray],
|
|
247
240
|
/,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
):
|
|
252
|
-
for _ in bio_skip_iter(bio, size, chunksize, callback=callback):
|
|
253
|
-
pass
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
async def bio_skip_bytes_async(
|
|
257
|
-
bio: SupportsRead[bytes] | Callable[[int], bytes | Awaitable[bytes]],
|
|
258
|
-
/,
|
|
259
|
-
size: int = -1,
|
|
260
|
-
chunksize: int = COPY_BUFSIZE,
|
|
261
|
-
callback: None | Callable[[int], Any] = None,
|
|
262
|
-
):
|
|
263
|
-
async for _ in bio_skip_async_iter(bio, size, chunksize, callback=callback):
|
|
264
|
-
pass
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def bytes_iter_to_reader(it: Iterable[bytes | bytearray], /) -> SupportsRead[bytes]:
|
|
268
|
-
get_next = iter(it).__next__
|
|
269
|
-
at_eof = False
|
|
241
|
+
) -> SupportsRead[bytes]:
|
|
242
|
+
getnext = iter(it).__next__
|
|
243
|
+
at_end = False
|
|
270
244
|
unconsumed: bytearray = bytearray(b"")
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
245
|
+
lock = Lock()
|
|
246
|
+
def read(n=-1, /) -> bytes:
|
|
247
|
+
nonlocal at_end, unconsumed
|
|
248
|
+
if at_end or n == 0:
|
|
274
249
|
return b""
|
|
275
|
-
|
|
276
|
-
|
|
250
|
+
with lock:
|
|
251
|
+
try:
|
|
252
|
+
if n is None or n < 0:
|
|
253
|
+
while True:
|
|
254
|
+
unconsumed += getnext()
|
|
255
|
+
else:
|
|
256
|
+
while n > len(unconsumed):
|
|
257
|
+
unconsumed += getnext()
|
|
258
|
+
b, unconsumed = bytes(unconsumed[:n]), unconsumed[n:]
|
|
259
|
+
return b
|
|
260
|
+
except StopIteration:
|
|
261
|
+
at_end = True
|
|
262
|
+
return bytes(unconsumed)
|
|
263
|
+
def readinto(buf, /) -> int:
|
|
264
|
+
nonlocal at_end, unconsumed
|
|
265
|
+
if at_end or not (bufsize := len(buf)):
|
|
266
|
+
return 0
|
|
267
|
+
with lock:
|
|
268
|
+
if bufsize <= len(unconsumed):
|
|
269
|
+
buf[:], unconsumed = unconsumed[:bufsize], unconsumed[bufsize:]
|
|
270
|
+
return bufsize
|
|
271
|
+
n = len(unconsumed)
|
|
272
|
+
buf[:n] = unconsumed
|
|
273
|
+
del unconsumed[:]
|
|
274
|
+
try:
|
|
277
275
|
while True:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
276
|
+
b = getnext()
|
|
277
|
+
if not b:
|
|
278
|
+
continue
|
|
279
|
+
m = n + len(b)
|
|
280
|
+
if m >= bufsize:
|
|
281
|
+
buf[n:] = b[:bufsize-n]
|
|
282
|
+
unconsumed += b[m-bufsize:]
|
|
283
|
+
return bufsize
|
|
284
|
+
else:
|
|
285
|
+
buf[n:m] = b
|
|
286
|
+
n = m
|
|
287
|
+
except StopIteration:
|
|
288
|
+
at_end = True
|
|
289
|
+
return n
|
|
290
|
+
def __next__() -> bytes:
|
|
291
|
+
nonlocal unconsumed, at_end
|
|
292
|
+
if at_end:
|
|
293
|
+
raise StopIteration
|
|
294
|
+
if unconsumed:
|
|
295
|
+
# search for b"\n"
|
|
296
|
+
if (idx := unconsumed.find(49)) > -1:
|
|
297
|
+
idx += 1
|
|
298
|
+
b, unconsumed = bytes(unconsumed[:idx]), unconsumed[idx:]
|
|
299
|
+
return b
|
|
300
|
+
try:
|
|
301
|
+
while True:
|
|
302
|
+
b = getnext()
|
|
303
|
+
if not b:
|
|
304
|
+
continue
|
|
305
|
+
if (idx := b.find(49)) > -1:
|
|
306
|
+
idx += 1
|
|
307
|
+
unconsumed += b[:idx]
|
|
308
|
+
b, unconsumed = bytes(unconsumed), bytearray(b[idx:])
|
|
309
|
+
return b
|
|
310
|
+
unconsumed += b
|
|
284
311
|
except StopIteration:
|
|
285
|
-
|
|
286
|
-
|
|
312
|
+
at_end = True
|
|
313
|
+
if unconsumed:
|
|
314
|
+
return bytes(unconsumed)
|
|
315
|
+
raise
|
|
287
316
|
reprs = f"<reader for {it!r}>"
|
|
288
|
-
return type("reader", (), {
|
|
317
|
+
return type("reader", (), {
|
|
318
|
+
"read": staticmethod(read),
|
|
319
|
+
"readinto": staticmethod(readinto),
|
|
320
|
+
"__iter__": lambda self, /: self,
|
|
321
|
+
"__next__": staticmethod(__next__),
|
|
322
|
+
"__repr__": staticmethod(lambda: reprs),
|
|
323
|
+
})()
|
|
289
324
|
|
|
290
325
|
|
|
291
|
-
def
|
|
326
|
+
def bytes_iter_to_async_reader(
|
|
327
|
+
it: Iterable[bytes | bytearray] | AsyncIterable[bytes | bytearray],
|
|
328
|
+
/,
|
|
329
|
+
threaded: bool = True,
|
|
330
|
+
) -> SupportsRead[bytes]:
|
|
292
331
|
if isinstance(it, AsyncIterable):
|
|
293
|
-
|
|
332
|
+
getnext = aiter(it).__anext__
|
|
294
333
|
else:
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
at_eof = False
|
|
334
|
+
getnext = ensure_async(iter(it).__next__, threaded=threaded)
|
|
335
|
+
at_end = False
|
|
298
336
|
unconsumed: bytearray = bytearray(b"")
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
337
|
+
lock = AsyncLock()
|
|
338
|
+
async def read(n=-1, /) -> bytes:
|
|
339
|
+
nonlocal at_end, unconsumed
|
|
340
|
+
if at_end or n == 0:
|
|
302
341
|
return b""
|
|
303
|
-
|
|
304
|
-
|
|
342
|
+
async with lock:
|
|
343
|
+
try:
|
|
344
|
+
if n is None or n < 0:
|
|
345
|
+
while True:
|
|
346
|
+
unconsumed += await getnext()
|
|
347
|
+
else:
|
|
348
|
+
while n > len(unconsumed):
|
|
349
|
+
unconsumed += await getnext()
|
|
350
|
+
b, unconsumed = bytes(unconsumed[:n]), unconsumed[n:]
|
|
351
|
+
return b
|
|
352
|
+
except StopAsyncIteration:
|
|
353
|
+
at_end = True
|
|
354
|
+
return bytes(unconsumed)
|
|
355
|
+
async def readinto(buf, /) -> int:
|
|
356
|
+
nonlocal at_end, unconsumed
|
|
357
|
+
if at_end or not (bufsize := len(buf)):
|
|
358
|
+
return 0
|
|
359
|
+
async with lock:
|
|
360
|
+
if bufsize <= len(unconsumed):
|
|
361
|
+
buf[:], unconsumed = unconsumed[:bufsize], unconsumed[bufsize:]
|
|
362
|
+
return bufsize
|
|
363
|
+
n = len(unconsumed)
|
|
364
|
+
buf[:n] = unconsumed
|
|
365
|
+
del unconsumed[:]
|
|
366
|
+
try:
|
|
305
367
|
while True:
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
368
|
+
b = await getnext()
|
|
369
|
+
if not b:
|
|
370
|
+
continue
|
|
371
|
+
m = n + len(b)
|
|
372
|
+
if m >= bufsize:
|
|
373
|
+
buf[n:] = b[:bufsize-n]
|
|
374
|
+
unconsumed += b[m-bufsize:]
|
|
375
|
+
return bufsize
|
|
376
|
+
else:
|
|
377
|
+
buf[n:m] = b
|
|
378
|
+
n = m
|
|
379
|
+
except StopAsyncIteration:
|
|
380
|
+
at_end = True
|
|
381
|
+
return n
|
|
382
|
+
async def __next__() -> bytes:
|
|
383
|
+
nonlocal unconsumed, at_end
|
|
384
|
+
if at_end:
|
|
385
|
+
raise StopIteration
|
|
386
|
+
if unconsumed:
|
|
387
|
+
# search for b"\n"
|
|
388
|
+
if (idx := unconsumed.find(49)) > -1:
|
|
389
|
+
idx += 1
|
|
390
|
+
b, unconsumed = bytes(unconsumed[:idx]), unconsumed[idx:]
|
|
391
|
+
return b
|
|
392
|
+
try:
|
|
393
|
+
while True:
|
|
394
|
+
b = await getnext()
|
|
395
|
+
if not b:
|
|
396
|
+
continue
|
|
397
|
+
if (idx := b.find(49)) > -1:
|
|
398
|
+
idx += 1
|
|
399
|
+
unconsumed += b[:idx]
|
|
400
|
+
b, unconsumed = bytes(unconsumed), bytearray(b[idx:])
|
|
401
|
+
return b
|
|
402
|
+
unconsumed += b
|
|
312
403
|
except StopIteration:
|
|
313
|
-
|
|
314
|
-
|
|
404
|
+
at_end = True
|
|
405
|
+
if unconsumed:
|
|
406
|
+
return bytes(unconsumed)
|
|
407
|
+
raise
|
|
315
408
|
reprs = f"<reader for {it!r}>"
|
|
316
|
-
return type("reader", (), {
|
|
409
|
+
return type("reader", (), {
|
|
410
|
+
"read": staticmethod(read),
|
|
411
|
+
"readinto": staticmethod(readinto),
|
|
412
|
+
"__iter__": lambda self, /: self,
|
|
413
|
+
"__next__": staticmethod(__next__),
|
|
414
|
+
"__repr__": staticmethod(lambda: reprs),
|
|
415
|
+
})()
|
|
317
416
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-filewrap
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Python file wrappers.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-filewrap
|
|
6
6
|
License: MIT
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
21
21
|
Classifier: Topic :: Software Development
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Dist: python-asynctools
|
|
24
25
|
Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-filewrap
|
|
25
26
|
Description-Content-Type: text/markdown
|
|
26
27
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
2
|
+
filewrap/__init__.py,sha256=28PmcNRaPhSDiWpDStAHTGguChOYdxRfAfezwLlH9z8,13273
|
|
3
|
+
filewrap/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
python_filewrap-0.0.4.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
+
python_filewrap-0.0.4.dist-info/METADATA,sha256=K12tl1IE20BhHOIhUhJXx0v-l5g1ws3_6S1iC1gLWB8,1362
|
|
6
|
+
python_filewrap-0.0.4.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
7
|
+
python_filewrap-0.0.4.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
2
|
-
filewrap/__init__.py,sha256=MzZLsQJkjBke4XZzxjw8i5eyL7LyiGjw3M4HVyiMxeA,9893
|
|
3
|
-
filewrap/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
python_filewrap-0.0.3.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
-
python_filewrap-0.0.3.1.dist-info/METADATA,sha256=OO_ZWnnc6HBBvRtFXfV2kPM3zBji3dsMTRSruLtGPiI,1331
|
|
6
|
-
python_filewrap-0.0.3.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
7
|
-
python_filewrap-0.0.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|