python-filewrap 0.0.3__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 +174 -71
- {python_filewrap-0.0.3.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.dist-info/RECORD +0 -7
- {python_filewrap-0.0.3.dist-info → python_filewrap-0.0.4.dist-info}/LICENSE +0 -0
- {python_filewrap-0.0.3.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,72 +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
|
-
|
|
253
|
-
|
|
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
|
-
unconsumed = bytearray(b"")
|
|
270
|
-
def read(n=-1):
|
|
271
|
-
nonlocal unconsumed
|
|
272
|
-
if n == 0:
|
|
241
|
+
) -> SupportsRead[bytes]:
|
|
242
|
+
getnext = iter(it).__next__
|
|
243
|
+
at_end = False
|
|
244
|
+
unconsumed: bytearray = bytearray(b"")
|
|
245
|
+
lock = Lock()
|
|
246
|
+
def read(n=-1, /) -> bytes:
|
|
247
|
+
nonlocal at_end, unconsumed
|
|
248
|
+
if at_end or n == 0:
|
|
273
249
|
return b""
|
|
274
|
-
|
|
275
|
-
|
|
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:
|
|
276
275
|
while True:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
|
283
311
|
except StopIteration:
|
|
284
|
-
|
|
312
|
+
at_end = True
|
|
313
|
+
if unconsumed:
|
|
314
|
+
return bytes(unconsumed)
|
|
315
|
+
raise
|
|
285
316
|
reprs = f"<reader for {it!r}>"
|
|
286
|
-
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
|
+
})()
|
|
287
324
|
|
|
288
325
|
|
|
289
|
-
def
|
|
290
|
-
|
|
326
|
+
def bytes_iter_to_async_reader(
|
|
327
|
+
it: Iterable[bytes | bytearray] | AsyncIterable[bytes | bytearray],
|
|
328
|
+
/,
|
|
329
|
+
threaded: bool = True,
|
|
330
|
+
) -> SupportsRead[bytes]:
|
|
291
331
|
if isinstance(it, AsyncIterable):
|
|
292
|
-
|
|
332
|
+
getnext = aiter(it).__anext__
|
|
293
333
|
else:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
334
|
+
getnext = ensure_async(iter(it).__next__, threaded=threaded)
|
|
335
|
+
at_end = False
|
|
336
|
+
unconsumed: bytearray = bytearray(b"")
|
|
337
|
+
lock = AsyncLock()
|
|
338
|
+
async def read(n=-1, /) -> bytes:
|
|
339
|
+
nonlocal at_end, unconsumed
|
|
340
|
+
if at_end or n == 0:
|
|
299
341
|
return b""
|
|
300
|
-
|
|
301
|
-
|
|
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:
|
|
302
367
|
while True:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
|
309
403
|
except StopIteration:
|
|
310
|
-
|
|
404
|
+
at_end = True
|
|
405
|
+
if unconsumed:
|
|
406
|
+
return bytes(unconsumed)
|
|
407
|
+
raise
|
|
311
408
|
reprs = f"<reader for {it!r}>"
|
|
312
|
-
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
|
+
})()
|
|
313
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=Q-2Vh8EycWNA2aEmNGKAJHHuEKpdn89JTmOKJWl21go,9765
|
|
3
|
-
filewrap/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
python_filewrap-0.0.3.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
-
python_filewrap-0.0.3.dist-info/METADATA,sha256=n-AMSKOYJZ4eiVIwDEeziJkFkCjW1Mkg6jJZSrXBiyo,1329
|
|
6
|
-
python_filewrap-0.0.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
7
|
-
python_filewrap-0.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|