python-filewrap 0.2.3.1__tar.gz → 0.2.6__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-filewrap
3
- Version: 0.2.3.1
3
+ Version: 0.2.6
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
@@ -17,12 +17,13 @@ Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
20
21
  Classifier: Programming Language :: Python :: 3 :: Only
21
22
  Classifier: Topic :: Software Development
22
23
  Classifier: Topic :: Software Development :: Libraries
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
25
  Requires-Dist: python-asynctools (>=0.0.5)
25
- Requires-Dist: python-property (>=0.0.2)
26
+ Requires-Dist: python-property (>=0.0.3)
26
27
  Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-filewrap
27
28
  Description-Content-Type: text/markdown
28
29
 
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env python3
2
2
  # encoding: utf-8
3
3
 
4
+ # TODO: 实现一种 memoryview,可以作为环形缓冲区使用
4
5
  # TODO: 使用 codecs.iterdecode 来避免解码过程中的一些重复操作
5
6
  # TODO: AsyncTextIOWrapper 的 read 和 readline 算法效率不高,因为会反复创建二进制对象,如果可以复用一段或者几段(内存块组)内存,则可以大大增加效率,还可以引入环形缓冲区(使用长度限定的 bytearray,之后所有操作在 memoryview 上进行,根据当前的可用区块开返回 memoryview),以减少内存分配的开销
6
7
  # TODO: AsyncTextIOWrapper.readline 有大量的字符串拼接操作,效率极低,需用 str.joins 方法优化
7
8
 
8
9
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
9
- __version__ = (0, 2, 3)
10
+ __version__ = (0, 2, 6)
10
11
  __all__ = [
11
12
  "Buffer", "SupportsRead", "SupportsReadinto", "SupportsWrite", "SupportsSeek",
12
13
  "AsyncBufferedReader", "AsyncTextIOWrapper",
@@ -21,8 +22,9 @@ __all__ = [
21
22
  "copyfileobj", "copyfileobj_async",
22
23
  ]
23
24
 
25
+ from array import array
24
26
  from asyncio import to_thread, Lock as AsyncLock
25
- from collections.abc import Awaitable, AsyncIterable, AsyncIterator, Callable, Iterable, Iterator
27
+ from collections.abc import Awaitable, AsyncIterable, AsyncIterator, Callable, Iterable, Iterator, Sized
26
28
  from functools import update_wrapper
27
29
  from io import BufferedIOBase, BufferedReader, BytesIO, RawIOBase, TextIOWrapper
28
30
  from inspect import isawaitable, iscoroutinefunction, isasyncgen, isgenerator
@@ -51,7 +53,7 @@ except ImportError:
51
53
  Buffer.register(array)
52
54
 
53
55
  from asynctools import async_chain, ensure_async, ensure_aiter, run_async
54
- from property import staticproperty
56
+ from property import funcproperty
55
57
 
56
58
 
57
59
  Args = ParamSpec("Args")
@@ -68,6 +70,19 @@ class VirtualBufferedReader:
68
70
  CRE_NOT_UNIX_NEWLINES_sub = re_compile("\r\n|\r").sub
69
71
 
70
72
 
73
+ def buffer_length(b: Buffer, /) -> int:
74
+ if isinstance(b, Sized):
75
+ return buffer_length(b)
76
+ else:
77
+ return buffer_length(memoryview(b))
78
+
79
+
80
+ def ensure_bytes(b: Buffer, /) -> array | bytes | bytearray | memoryview:
81
+ if isinstance(b, (array, bytes, bytearray, memoryview)):
82
+ return b
83
+ return memoryview(b)
84
+
85
+
71
86
  @runtime_checkable
72
87
  class SupportsRead(Protocol[_T_co]):
73
88
  def read(self, /, __length: int = ...) -> _T_co: ...
@@ -91,9 +106,9 @@ class SupportsSeek(Protocol):
91
106
  # TODO: 一些特定编码的 bom 用字典写死,编码名可以规范化,用 codecs.lookup(encoding).name
92
107
  def get_bom(encoding: str) -> bytes:
93
108
  code = memoryview(bytes("a", encoding))
94
- if len(code) == 1:
109
+ if buffer_length(code) == 1:
95
110
  return b""
96
- for i in range(1, len(code)):
111
+ for i in range(1, buffer_length(code)):
97
112
  try:
98
113
  str(code[:i], encoding)
99
114
  return code[:i].tobytes()
@@ -226,13 +241,13 @@ class AsyncBufferedReader(BufferedReader):
226
241
  self._pos += buf_size
227
242
  buf_size += await self.readinto(buffer_view[buf_size:])
228
243
  return buffer_view[:buf_size].tobytes()
229
- BUFSIZE = len(buf_view)
244
+ BUFSIZE = buffer_length(buf_view)
230
245
  read = ensure_async(self.raw.read, threaded=True)
231
246
  buffer = bytearray(buf_view[buf_pos:buf_stop])
232
247
  try:
233
248
  while data := await read(BUFSIZE):
234
249
  buffer += data
235
- length = len(data)
250
+ length = buffer_length(data)
236
251
  self._pos += length
237
252
  if BUFSIZE == length:
238
253
  buf_view[:] = data
@@ -278,9 +293,9 @@ class AsyncBufferedReader(BufferedReader):
278
293
  raise
279
294
  prev_data = buf_view[buf_pos:buf_stop].tobytes()
280
295
  if data:
281
- BUFSIZE = len(buf_view)
282
- length = len(data)
283
- self._pos += len(prev_data) + length
296
+ BUFSIZE = buffer_length(buf_view)
297
+ length = buffer_length(data)
298
+ self._pos += buffer_length(prev_data) + length
284
299
  if BUFSIZE <= length:
285
300
  buf_view[:] = memoryview(data)[-BUFSIZE:]
286
301
  self._buf_pos = self._buf_stop = BUFSIZE
@@ -301,7 +316,7 @@ class AsyncBufferedReader(BufferedReader):
301
316
  async def readinto(self, buffer, /) -> int: # type: ignore
302
317
  if self.closed:
303
318
  raise ValueError("I/O operation on closed file.")
304
- size = len(buffer)
319
+ size = buffer_length(buffer)
305
320
  if size == 0:
306
321
  return 0
307
322
  buf_view = self._buf_view
@@ -319,14 +334,14 @@ class AsyncBufferedReader(BufferedReader):
319
334
  except AttributeError:
320
335
  read = ensure_async(self.raw.read, threaded=True)
321
336
  async def readinto(buffer, /) -> int:
322
- data = await read(len(buffer))
337
+ data = await read(buffer_length(buffer))
323
338
  if data:
324
- size = len(data)
339
+ size = buffer_length(data)
325
340
  buffer[:size] = data
326
341
  return size
327
342
  else:
328
343
  return 0
329
- BUFSIZE = len(buf_view)
344
+ BUFSIZE = buffer_length(buf_view)
330
345
  buffer_view = memoryview(buffer)
331
346
  buffer_view[:buf_size] = buf_view[buf_pos:buf_stop]
332
347
  buf_pos = self._buf_pos = buf_stop
@@ -349,7 +364,7 @@ class AsyncBufferedReader(BufferedReader):
349
364
  break
350
365
  if length < BUFSIZE:
351
366
  part1, part2 = buf_view[length:].tobytes(), buf_view[:length].tobytes()
352
- index = len(part1)
367
+ index = buffer_length(part1)
353
368
  buf_view[:index] = part1
354
369
  buf_view[index:] = part2
355
370
  running = False
@@ -373,7 +388,7 @@ class AsyncBufferedReader(BufferedReader):
373
388
  async def readinto1(self, buffer, /) -> int: # type: ignore
374
389
  if self.closed:
375
390
  raise ValueError("I/O operation on closed file.")
376
- size = len(buffer)
391
+ size = buffer_length(buffer)
377
392
  if size == 0:
378
393
  return 0
379
394
  buf_view = self._buf_view
@@ -391,14 +406,14 @@ class AsyncBufferedReader(BufferedReader):
391
406
  except AttributeError:
392
407
  read = ensure_async(self.raw.read, threaded=True)
393
408
  async def readinto(buffer, /) -> int:
394
- data = await read(len(buffer))
409
+ data = await read(buffer_length(buffer))
395
410
  if data:
396
- size = len(data)
411
+ size = buffer_length(data)
397
412
  buffer[:size] = data
398
413
  return size
399
414
  else:
400
415
  return 0
401
- BUFSIZE = len(buf_view)
416
+ BUFSIZE = buffer_length(buf_view)
402
417
  buffer_view = memoryview(buffer)
403
418
  buffer_view[:buf_size] = buf_view[buf_pos:buf_stop]
404
419
  buf_pos = self._buf_pos = buf_stop
@@ -411,7 +426,7 @@ class AsyncBufferedReader(BufferedReader):
411
426
  self.calibrate()
412
427
  raise
413
428
  if length:
414
- BUFSIZE = len(buf_view)
429
+ BUFSIZE = buffer_length(buf_view)
415
430
  buffer_pos += length
416
431
  self._pos += length
417
432
  if BUFSIZE <= buffer_pos:
@@ -485,10 +500,10 @@ class AsyncBufferedReader(BufferedReader):
485
500
  self.calibrate()
486
501
  raise
487
502
  buf_view = self._buf_view
488
- BUFSIZE = len(buf_view)
489
- length = len(data)
503
+ BUFSIZE = buffer_length(buf_view)
504
+ length = buffer_length(data)
490
505
  prev_data = buf_view[buf_pos:buf_stop].tobytes()
491
- self._pos += len(prev_data) + length
506
+ self._pos += buffer_length(prev_data) + length
492
507
  if BUFSIZE <= length:
493
508
  buf_view[:] = memoryview(data)[-BUFSIZE:]
494
509
  self._buf_pos = self._buf_stop = BUFSIZE
@@ -516,7 +531,7 @@ class AsyncBufferedReader(BufferedReader):
516
531
  else:
517
532
  while hint > 0 and (line := await readline()):
518
533
  append(line)
519
- hint -= len(line)
534
+ hint -= buffer_length(line)
520
535
  return lines
521
536
 
522
537
  async def seek(self, target: int, whence: int = 0, /) -> int: # type: ignore
@@ -629,7 +644,9 @@ class AsyncTextIOWrapper(TextIOWrapper):
629
644
  data = await read(-1)
630
645
  else:
631
646
  data = await read(size)
632
- if size < 0 or len(data) < size:
647
+ if not isinstance(data, Sized):
648
+ data = memoryview(data)
649
+ if size < 0 or buffer_length(data) < size:
633
650
  text = str(data, encoding, errors)
634
651
  if newline is None:
635
652
  text = CRE_NOT_UNIX_NEWLINES_sub("\n", text)
@@ -644,8 +661,10 @@ class AsyncTextIOWrapper(TextIOWrapper):
644
661
 
645
662
  ls_parts: list[str] = []
646
663
  add_part = ls_parts.append
647
- cache = data
648
- while size and len(data) == size:
664
+ if not isinstance(data, Sized):
665
+ data = memoryview(data)
666
+ cache = bytes(data)
667
+ while size and buffer_length(data) == size:
649
668
  while cache:
650
669
  try:
651
670
  size -= process_part(cache)
@@ -655,11 +674,11 @@ class AsyncTextIOWrapper(TextIOWrapper):
655
674
  if start:
656
675
  size -= process_part(cache[:start])
657
676
  if e.reason == "truncated data":
658
- if stop == len(cache):
677
+ if stop == buffer_length(cache):
659
678
  cache = cache[start:]
660
679
  break
661
680
  else:
662
- while stop < len(cache):
681
+ while stop < buffer_length(cache):
663
682
  stop += 1
664
683
  try:
665
684
  size -= process_part(cache[start:stop])
@@ -670,13 +689,13 @@ class AsyncTextIOWrapper(TextIOWrapper):
670
689
  e = exc
671
690
  if e.reason != "truncated data":
672
691
  break
673
- if stop == len(cache):
692
+ if stop == buffer_length(cache):
674
693
  cache = cache[start:]
675
694
  break_this_loop = True
676
695
  break
677
696
  if break_this_loop:
678
697
  break
679
- elif e.reason == "unexpected end of data" and stop == len(cache):
698
+ elif e.reason == "unexpected end of data" and stop == buffer_length(cache):
680
699
  cache = cache[start:]
681
700
  break
682
701
  if errors == "strict":
@@ -684,6 +703,8 @@ class AsyncTextIOWrapper(TextIOWrapper):
684
703
  size -= process_part(cache[start:stop], errors)
685
704
  cache = cache[stop:]
686
705
  data = await read(size)
706
+ if not isinstance(data, Sized):
707
+ data = memoryview(data)
687
708
  cache += data
688
709
  if cache:
689
710
  process_part(cache, errors)
@@ -714,7 +735,7 @@ class AsyncTextIOWrapper(TextIOWrapper):
714
735
  if bom := self._bom:
715
736
  crb = crb.removeprefix(bom)
716
737
  lfb = lfb.removeprefix(bom)
717
- lfb_len = len(lfb)
738
+ lfb_len = buffer_length(lfb)
718
739
  buf = bytearray()
719
740
  text = ""
720
741
  reach_end = False
@@ -730,35 +751,38 @@ class AsyncTextIOWrapper(TextIOWrapper):
730
751
  break
731
752
  elif buf.endswith(crb):
732
753
  peek_maybe_lfb = await read(lfb_len)
754
+ if not isinstance(peek_maybe_lfb, Sized):
755
+ peek_maybe_lfb = memoryview(peek_maybe_lfb)
733
756
  if peek_maybe_lfb == lfb:
734
757
  buf += lfb
735
758
  elif peek_maybe_lfb:
736
759
  # TODO: 这是一个提前量,未必需要立即往回 seek,因为转换为 str 后可能尾部不是 \r(因为可以和前面的符号结合),所以这个可能可以被复用,如果需要优化,可以在程序结束时的 finally 部分最终执行 seek(可能最终字符被消耗所以不需要 seek)
737
- await seek(-len(peek_maybe_lfb), 1)
738
- if len(peek_maybe_lfb) < lfb_len:
760
+ o = buffer_length(peek_maybe_lfb)
761
+ await seek(-o, 1)
762
+ if o < lfb_len:
739
763
  reach_end = True
740
764
  break
741
765
  else:
742
766
  reach_end = True
743
767
  else:
744
768
  while True:
745
- buf_stop = len(buf)
769
+ buf_stop = buffer_length(buf)
746
770
  peek_b = peek()
747
771
  if peek_b:
748
772
  buf += peek_b
749
773
  if newline:
750
774
  if (idx := buf.find(sepb)) > -1:
751
- idx += len(sepb)
775
+ idx += buffer_length(sepb)
752
776
  await read(idx - buf_stop)
753
777
  del buf[idx:]
754
778
  break
755
779
  elif (idx := buf.find(lfb)) > -1:
756
- idx += len(lfb)
780
+ idx += buffer_length(lfb)
757
781
  await read(idx - buf_stop)
758
782
  del buf[idx:]
759
783
  break
760
784
  elif (idx := buf.find(crb)) > -1:
761
- idx += len(crb)
785
+ idx += buffer_length(crb)
762
786
  await read(idx - buf_stop)
763
787
  if buf.startswith(lfb, idx):
764
788
  await read(lfb_len)
@@ -767,7 +791,7 @@ class AsyncTextIOWrapper(TextIOWrapper):
767
791
  del buf[idx:]
768
792
  break
769
793
  if peek_b:
770
- await read(len(peek_b))
794
+ await read(buffer_length(peek_b))
771
795
  c = await read(1)
772
796
  if not c:
773
797
  reach_end = True
@@ -782,11 +806,11 @@ class AsyncTextIOWrapper(TextIOWrapper):
782
806
  if start:
783
807
  text += str(buf[:start], encoding)
784
808
  if e.reason == "truncated data":
785
- if stop == len(buf):
809
+ if stop == buffer_length(buf):
786
810
  buf = buf[start:]
787
811
  break
788
812
  else:
789
- while stop < len(buf):
813
+ while stop < buffer_length(buf):
790
814
  stop += 1
791
815
  try:
792
816
  text += str(buf[start:stop], encoding)
@@ -797,13 +821,13 @@ class AsyncTextIOWrapper(TextIOWrapper):
797
821
  e = exc
798
822
  if e.reason != "truncated data":
799
823
  break
800
- if stop == len(buf):
824
+ if stop == buffer_length(buf):
801
825
  buf = buf[start:]
802
826
  break_this_loop = True
803
827
  break
804
828
  if break_this_loop:
805
829
  break
806
- if e.reason == "unexpected end of data" and stop == len(buf):
830
+ if e.reason == "unexpected end of data" and stop == buffer_length(buf):
807
831
  buf = buf[start:]
808
832
  break
809
833
  if errors == "strict":
@@ -839,26 +863,29 @@ class AsyncTextIOWrapper(TextIOWrapper):
839
863
  break
840
864
  elif buf.endswith(crb):
841
865
  peek_maybe_lfb = await read(lfb_len)
866
+ if not isinstance(peek_maybe_lfb, Sized):
867
+ peek_maybe_lfb = memoryview(peek_maybe_lfb)
842
868
  if peek_maybe_lfb == lfb:
843
869
  buf += lfb
844
870
  elif peek_maybe_lfb:
845
- await seek(-len(peek_maybe_lfb), 1)
846
- if len(peek_maybe_lfb) < lfb_len:
871
+ o = buffer_length(peek_maybe_lfb)
872
+ await seek(-o, 1)
873
+ if o < lfb_len:
847
874
  reach_end = True
848
875
  break
849
876
  else:
850
877
  reach_end = True
851
878
  else:
852
879
  while rem:
853
- buf_stop = len(buf)
880
+ buf_stop = buffer_length(buf)
854
881
  peek_b = peek()
855
882
  if peek_b:
856
- if len(peek_b) >= rem:
883
+ if buffer_length(peek_b) >= rem:
857
884
  buf += peek_b[:rem]
858
885
  rem = 0
859
886
  else:
860
887
  buf += peek_b
861
- rem -= len(peek_b)
888
+ rem -= buffer_length(peek_b)
862
889
  if newline:
863
890
  if (idx := buf.find(sepb)) > -1:
864
891
  idx += 1
@@ -894,7 +921,7 @@ class AsyncTextIOWrapper(TextIOWrapper):
894
921
  start, stop = e.start, e.end
895
922
  if start:
896
923
  text += str(buf[:start], encoding)
897
- if e.reason in ("unexpected end of data", "truncated data") and stop == len(buf):
924
+ if e.reason in ("unexpected end of data", "truncated data") and stop == buffer_length(buf):
898
925
  buf = buf[start:]
899
926
  break
900
927
  if errors == "strict":
@@ -951,13 +978,13 @@ class AsyncTextIOWrapper(TextIOWrapper):
951
978
  self.newline = newline
952
979
 
953
980
  async def seek(self, target: int, whence: int = 0, /) -> int: # type: ignore
954
- return await ensure_async(self.buffer.seek, threaded=True)(target, whence)
981
+ return await ensure_async(getattr(self.buffer, "seek"), threaded=True)(target, whence)
955
982
 
956
983
  def tell(self, /) -> int:
957
- return self.buffer.tell()
984
+ return getattr(self.buffer, "tell")()
958
985
 
959
986
  async def truncate(self, pos: None | int = None, /) -> int: # type: ignore
960
- return await ensure_async(self.buffer.truncate, threaded=True)(pos)
987
+ return await ensure_async(getattr(self.buffer, "truncate"), threaded=True)(pos)
961
988
 
962
989
  async def write(self, text: str, /) -> int: # type: ignore
963
990
  match self.newline:
@@ -1015,7 +1042,7 @@ def bio_chunk_iter(
1015
1042
  length = readinto(buf)
1016
1043
  if callback:
1017
1044
  callback(length)
1018
- if length < len(buf):
1045
+ if length < buffer_length(buf):
1019
1046
  del buf[length:]
1020
1047
  yield buf
1021
1048
  break
@@ -1035,7 +1062,7 @@ def bio_chunk_iter(
1035
1062
  while size:
1036
1063
  readsize = min(chunksize, size)
1037
1064
  chunk = read(readsize)
1038
- length = len(chunk)
1065
+ length = buffer_length(chunk)
1039
1066
  if callback:
1040
1067
  callback(length)
1041
1068
  yield chunk
@@ -1045,12 +1072,12 @@ def bio_chunk_iter(
1045
1072
  elif size < 0:
1046
1073
  while (chunk := read(chunksize)):
1047
1074
  if callback:
1048
- callback(len(chunk))
1075
+ callback(buffer_length(chunk))
1049
1076
  yield chunk
1050
1077
 
1051
1078
 
1052
1079
  async def bio_chunk_async_iter(
1053
- bio: SupportsRead[Buffer] | SupportsReadinto | Callable[[int], Buffer | Awaitable[Buffer]],
1080
+ bio: SupportsRead[Buffer] | SupportsRead[Awaitable[Buffer]] | SupportsReadinto | Callable[[int], Buffer | Awaitable[Buffer]],
1054
1081
  /,
1055
1082
  size: int = -1,
1056
1083
  chunksize: int = COPY_BUFSIZE,
@@ -1083,7 +1110,7 @@ async def bio_chunk_async_iter(
1083
1110
  length = await readinto(buf)
1084
1111
  if callback:
1085
1112
  await callback(length)
1086
- if length < len(buf):
1113
+ if length < buffer_length(buf):
1087
1114
  del buf[length:]
1088
1115
  yield buf
1089
1116
  break
@@ -1103,7 +1130,7 @@ async def bio_chunk_async_iter(
1103
1130
  while size:
1104
1131
  readsize = min(chunksize, size)
1105
1132
  chunk = await read(readsize)
1106
- length = len(chunk)
1133
+ length = buffer_length(chunk)
1107
1134
  if callback:
1108
1135
  await callback(length)
1109
1136
  yield chunk
@@ -1113,7 +1140,7 @@ async def bio_chunk_async_iter(
1113
1140
  elif size < 0:
1114
1141
  while (chunk := (await read(chunksize))):
1115
1142
  if callback:
1116
- await callback(len(chunk))
1143
+ await callback(buffer_length(chunk))
1117
1144
  yield chunk
1118
1145
 
1119
1146
 
@@ -1170,7 +1197,7 @@ def bio_skip_iter(
1170
1197
  if size > 0:
1171
1198
  while size:
1172
1199
  readsize = min(chunksize, size)
1173
- length = len(read(readsize))
1200
+ length = buffer_length(read(readsize))
1174
1201
  if callback:
1175
1202
  callback(length)
1176
1203
  yield length
@@ -1178,7 +1205,7 @@ def bio_skip_iter(
1178
1205
  break
1179
1206
  size -= readsize
1180
1207
  else:
1181
- while (length := len(read(chunksize))):
1208
+ while (length := buffer_length(read(chunksize))):
1182
1209
  if callback:
1183
1210
  callback(length)
1184
1211
  yield length
@@ -1189,7 +1216,7 @@ def bio_skip_iter(
1189
1216
 
1190
1217
 
1191
1218
  async def bio_skip_async_iter(
1192
- bio: SupportsRead[Buffer] | SupportsReadinto | Callable[[int], Buffer | Awaitable[Buffer]],
1219
+ bio: SupportsRead[Buffer] | SupportsRead[Awaitable[Buffer]] | SupportsReadinto | Callable[[int], Buffer | Awaitable[Buffer]],
1193
1220
  /,
1194
1221
  size: int = -1,
1195
1222
  chunksize: int = COPY_BUFSIZE,
@@ -1240,7 +1267,7 @@ async def bio_skip_async_iter(
1240
1267
  if size > 0:
1241
1268
  while size:
1242
1269
  readsize = min(chunksize, size)
1243
- length = len(await read(readsize))
1270
+ length = buffer_length(await read(readsize))
1244
1271
  if callback:
1245
1272
  await callback(length)
1246
1273
  yield length
@@ -1248,7 +1275,7 @@ async def bio_skip_async_iter(
1248
1275
  break
1249
1276
  size -= readsize
1250
1277
  else:
1251
- while (length := len(await read(chunksize))):
1278
+ while (length := buffer_length(await read(chunksize))):
1252
1279
  if callback:
1253
1280
  await callback(length)
1254
1281
  yield length
@@ -1271,7 +1298,7 @@ def bytes_iter(
1271
1298
  elif size == 0:
1272
1299
  return
1273
1300
  for b in it:
1274
- l = len(b)
1301
+ l = buffer_length(b)
1275
1302
  if l <= size:
1276
1303
  yield b
1277
1304
  if callback is not None:
@@ -1303,7 +1330,7 @@ async def bytes_async_iter(
1303
1330
  return
1304
1331
  callback = ensure_async(callback) if callable(callback) else None
1305
1332
  async for b in it:
1306
- l = len(b)
1333
+ l = buffer_length(b)
1307
1334
  if l <= size:
1308
1335
  yield b
1309
1336
  if callback is not None:
@@ -1330,8 +1357,9 @@ def bytes_iter_skip(
1330
1357
  return it
1331
1358
  if not callable(callback):
1332
1359
  callback = None
1333
- for m in map(memoryview, it):
1334
- l = len(m)
1360
+ m: memoryview
1361
+ for m in map(memoryview, it): # type: ignore
1362
+ l = buffer_length(m)
1335
1363
  if callback:
1336
1364
  callback(min(l, size))
1337
1365
  if l == size:
@@ -1356,7 +1384,7 @@ async def bytes_async_iter_skip(
1356
1384
  callback = ensure_async(callback) if callable(callback) else None
1357
1385
  async for b in it:
1358
1386
  m = memoryview(b)
1359
- l = len(m)
1387
+ l = buffer_length(m)
1360
1388
  if callback:
1361
1389
  await callback(min(l, size))
1362
1390
  if l == size:
@@ -1405,23 +1433,23 @@ def bytes_iter_to_reader(
1405
1433
  while True:
1406
1434
  unconsumed += getnext()
1407
1435
  else:
1408
- while n > len(unconsumed):
1436
+ while n > buffer_length(unconsumed):
1409
1437
  unconsumed += getnext()
1410
1438
  b, unconsumed = unconsumed[:n], unconsumed[n:]
1411
- pos += len(b)
1439
+ pos += buffer_length(b)
1412
1440
  return b
1413
1441
  except StopIteration:
1414
1442
  at_end = True
1415
1443
  b = unconsumed[:]
1416
1444
  del unconsumed[:]
1417
- pos += len(b)
1445
+ pos += buffer_length(b)
1418
1446
  return b
1419
1447
  def readinto(buf, /) -> int:
1420
1448
  nonlocal pos, at_end, unconsumed
1421
- if at_end or not (bufsize := len(buf)):
1449
+ if at_end or not (bufsize := buffer_length(buf)):
1422
1450
  return 0
1423
1451
  with lock:
1424
- n = len(unconsumed)
1452
+ n = buffer_length(unconsumed)
1425
1453
  if bufsize <= n:
1426
1454
  buf[:], unconsumed = unconsumed[:bufsize], unconsumed[bufsize:]
1427
1455
  pos += bufsize
@@ -1430,10 +1458,10 @@ def bytes_iter_to_reader(
1430
1458
  del unconsumed[:]
1431
1459
  try:
1432
1460
  while True:
1433
- b = getnext()
1461
+ b = ensure_bytes(getnext())
1434
1462
  if not b:
1435
1463
  continue
1436
- m = n + len(b)
1464
+ m = n + buffer_length(b)
1437
1465
  if m >= bufsize:
1438
1466
  buf[n:] = b[:bufsize-n]
1439
1467
  unconsumed += b[bufsize-n:]
@@ -1441,7 +1469,7 @@ def bytes_iter_to_reader(
1441
1469
  return bufsize
1442
1470
  else:
1443
1471
  buf[n:m] = b
1444
- pos += len(b)
1472
+ pos += buffer_length(b)
1445
1473
  n = m
1446
1474
  except StopIteration:
1447
1475
  at_end = True
@@ -1461,12 +1489,12 @@ def bytes_iter_to_reader(
1461
1489
  b, unconsumed = unconsumed[:idx], unconsumed[idx:]
1462
1490
  pos += idx
1463
1491
  return b
1464
- if n > 0 and len(unconsumed) >= n:
1492
+ if n > 0 and buffer_length(unconsumed) >= n:
1465
1493
  b, unconsumed = unconsumed[:n], unconsumed[n:]
1466
1494
  pos += n
1467
1495
  return b
1468
1496
  try:
1469
- start = len(unconsumed)
1497
+ start = buffer_length(unconsumed)
1470
1498
  while True:
1471
1499
  r = getnext()
1472
1500
  if not r:
@@ -1478,7 +1506,7 @@ def bytes_iter_to_reader(
1478
1506
  b, unconsumed = unconsumed[:idx], unconsumed[idx:]
1479
1507
  pos += idx
1480
1508
  return b
1481
- start = len(unconsumed)
1509
+ start = buffer_length(unconsumed)
1482
1510
  if n > 0 and start >= n:
1483
1511
  b, unconsumed = unconsumed[:n], unconsumed[n:]
1484
1512
  pos += n
@@ -1488,7 +1516,7 @@ def bytes_iter_to_reader(
1488
1516
  if unconsumed:
1489
1517
  b = unconsumed[:]
1490
1518
  del unconsumed[:]
1491
- pos += len(b)
1519
+ pos += buffer_length(b)
1492
1520
  return b
1493
1521
  raise
1494
1522
  def readlines(hint: int = -1, /) -> list[bytearray]:
@@ -1502,7 +1530,7 @@ def bytes_iter_to_reader(
1502
1530
  else:
1503
1531
  while hint > 0 and (line := readline()):
1504
1532
  append(line)
1505
- hint -= len(line)
1533
+ hint -= buffer_length(line)
1506
1534
  return lines
1507
1535
  def __next__() -> bytearray:
1508
1536
  if at_end or not (b := readline()):
@@ -1516,7 +1544,7 @@ def bytes_iter_to_reader(
1516
1544
  "__next__": staticmethod(__next__),
1517
1545
  "__repr__": staticmethod(lambda: reprs),
1518
1546
  "close": staticmethod(close),
1519
- "closed": staticproperty(lambda: at_end),
1547
+ "closed": funcproperty(staticmethod(lambda: at_end)),
1520
1548
  "peek": staticmethod(peek),
1521
1549
  "read": staticmethod(read),
1522
1550
  "readinto": staticmethod(readinto),
@@ -1532,7 +1560,7 @@ def bytes_iter_to_async_reader(
1532
1560
  it: Iterable[Buffer] | AsyncIterable[Buffer],
1533
1561
  /,
1534
1562
  threaded: bool = True,
1535
- ) -> SupportsRead[bytearray]:
1563
+ ) -> SupportsRead[Awaitable[bytearray]]:
1536
1564
  if isinstance(it, AsyncIterable):
1537
1565
  getnext = aiter(it).__anext__
1538
1566
  else:
@@ -1582,23 +1610,23 @@ def bytes_iter_to_async_reader(
1582
1610
  while True:
1583
1611
  unconsumed += await getnext()
1584
1612
  else:
1585
- while n > len(unconsumed):
1613
+ while n > buffer_length(unconsumed):
1586
1614
  unconsumed += await getnext()
1587
1615
  b, unconsumed = unconsumed[:n], unconsumed[n:]
1588
- pos += len(b)
1616
+ pos += buffer_length(b)
1589
1617
  return b
1590
1618
  except (StopIteration, StopAsyncIteration):
1591
1619
  at_end = True
1592
1620
  b = unconsumed[:]
1593
1621
  del unconsumed[:]
1594
- pos += len(b)
1622
+ pos += buffer_length(b)
1595
1623
  return b
1596
1624
  async def readinto(buf, /) -> int:
1597
1625
  nonlocal pos, at_end, unconsumed
1598
- if at_end or not (bufsize := len(buf)):
1626
+ if at_end or not (bufsize := buffer_length(buf)):
1599
1627
  return 0
1600
1628
  async with lock:
1601
- n = len(unconsumed)
1629
+ n = buffer_length(unconsumed)
1602
1630
  if bufsize <= n:
1603
1631
  buf[:], unconsumed = unconsumed[:bufsize], unconsumed[bufsize:]
1604
1632
  pos += bufsize
@@ -1607,10 +1635,10 @@ def bytes_iter_to_async_reader(
1607
1635
  del unconsumed[:]
1608
1636
  try:
1609
1637
  while True:
1610
- b = await getnext()
1638
+ b = ensure_bytes(await getnext())
1611
1639
  if not b:
1612
1640
  continue
1613
- m = n + len(b)
1641
+ m = n + buffer_length(b)
1614
1642
  if m >= bufsize:
1615
1643
  buf[n:] = b[:bufsize-n]
1616
1644
  unconsumed += b[bufsize-n:]
@@ -1618,7 +1646,7 @@ def bytes_iter_to_async_reader(
1618
1646
  return bufsize
1619
1647
  else:
1620
1648
  buf[n:m] = b
1621
- pos += len(b)
1649
+ pos += buffer_length(b)
1622
1650
  n = m
1623
1651
  except (StopIteration, StopAsyncIteration):
1624
1652
  at_end = True
@@ -1638,12 +1666,12 @@ def bytes_iter_to_async_reader(
1638
1666
  b, unconsumed = unconsumed[:idx], unconsumed[idx:]
1639
1667
  pos += idx
1640
1668
  return b
1641
- if n > 0 and len(unconsumed) >= n:
1669
+ if n > 0 and buffer_length(unconsumed) >= n:
1642
1670
  b, unconsumed = unconsumed[:n], unconsumed[n:]
1643
1671
  pos += n
1644
1672
  return b
1645
1673
  try:
1646
- start = len(unconsumed)
1674
+ start = buffer_length(unconsumed)
1647
1675
  while True:
1648
1676
  r = await getnext()
1649
1677
  if not r:
@@ -1655,7 +1683,7 @@ def bytes_iter_to_async_reader(
1655
1683
  b, unconsumed = unconsumed[:idx], unconsumed[idx:]
1656
1684
  pos += idx
1657
1685
  return b
1658
- start = len(unconsumed)
1686
+ start = buffer_length(unconsumed)
1659
1687
  if n > 0 and start >= n:
1660
1688
  b, unconsumed = unconsumed[:n], unconsumed[n:]
1661
1689
  pos += n
@@ -1665,7 +1693,7 @@ def bytes_iter_to_async_reader(
1665
1693
  if unconsumed:
1666
1694
  b = unconsumed[:]
1667
1695
  del unconsumed[:]
1668
- pos += len(b)
1696
+ pos += buffer_length(b)
1669
1697
  return b
1670
1698
  raise
1671
1699
  async def readlines(hint: int = -1, /) -> list[bytearray]:
@@ -1682,7 +1710,7 @@ def bytes_iter_to_async_reader(
1682
1710
  else:
1683
1711
  while hint > 0 and (line := await readline()):
1684
1712
  append(line)
1685
- hint -= len(line)
1713
+ hint -= buffer_length(line)
1686
1714
  return lines
1687
1715
  async def __anext__() -> bytearray:
1688
1716
  if at_end or not (b := await readline()):
@@ -1697,7 +1725,7 @@ def bytes_iter_to_async_reader(
1697
1725
  "__repr__": staticmethod(lambda: reprs),
1698
1726
  "close": staticmethod(close),
1699
1727
  "aclose": staticmethod(aclose),
1700
- "closed": staticproperty(lambda: at_end),
1728
+ "closed": funcproperty(staticmethod(lambda: at_end)),
1701
1729
  "peek": staticmethod(peek),
1702
1730
  "read": staticmethod(read),
1703
1731
  "readinto": staticmethod(readinto),
@@ -1715,7 +1743,7 @@ def bytes_to_chunk_iter(
1715
1743
  chunksize: int = COPY_BUFSIZE,
1716
1744
  ) -> Iterator[memoryview]:
1717
1745
  m = memoryview(b)
1718
- for i in range(0, len(m), chunksize):
1746
+ for i in range(0, buffer_length(m), chunksize):
1719
1747
  yield m[i:i+chunksize]
1720
1748
 
1721
1749
 
@@ -1725,7 +1753,7 @@ async def bytes_to_chunk_async_iter(
1725
1753
  chunksize: int = COPY_BUFSIZE,
1726
1754
  ) -> AsyncIterator[memoryview]:
1727
1755
  m = memoryview(b)
1728
- for i in range(0, len(m), chunksize):
1756
+ for i in range(0, buffer_length(m), chunksize):
1729
1757
  yield m[i:i+chunksize]
1730
1758
 
1731
1759
 
@@ -1737,7 +1765,7 @@ def bytes_ensure_part_iter(
1737
1765
  n = partsize
1738
1766
  for b in it:
1739
1767
  m = memoryview(b)
1740
- l = len(m)
1768
+ l = buffer_length(m)
1741
1769
  if l <= n:
1742
1770
  yield b
1743
1771
  if l == n:
@@ -1747,12 +1775,12 @@ def bytes_ensure_part_iter(
1747
1775
  else:
1748
1776
  yield m[:n]
1749
1777
  m = m[n:]
1750
- while len(m) >= partsize:
1778
+ while buffer_length(m) >= partsize:
1751
1779
  yield m[:partsize]
1752
1780
  m = m[partsize:]
1753
1781
  if m:
1754
1782
  yield m
1755
- n = partsize - len(m)
1783
+ n = partsize - buffer_length(m)
1756
1784
  else:
1757
1785
  n = partsize
1758
1786
 
@@ -1765,7 +1793,7 @@ async def bytes_ensure_part_async_iter(
1765
1793
  n = partsize
1766
1794
  async for b in ensure_aiter(it):
1767
1795
  m = memoryview(b)
1768
- l = len(m)
1796
+ l = buffer_length(m)
1769
1797
  if l <= n:
1770
1798
  yield b
1771
1799
  if l == n:
@@ -1775,12 +1803,12 @@ async def bytes_ensure_part_async_iter(
1775
1803
  else:
1776
1804
  yield m[:n]
1777
1805
  m = m[n:]
1778
- while len(m) >= partsize:
1806
+ while buffer_length(m) >= partsize:
1779
1807
  yield m[:partsize]
1780
1808
  m = m[partsize:]
1781
1809
  if m:
1782
1810
  yield m
1783
- n = partsize - len(m)
1811
+ n = partsize - buffer_length(m)
1784
1812
  else:
1785
1813
  n = partsize
1786
1814
 
@@ -1809,7 +1837,7 @@ def progress_bytes_iter(
1809
1837
  if callable(update_progress):
1810
1838
  for chunk in it:
1811
1839
  yield chunk
1812
- update_progress(len(chunk))
1840
+ update_progress(buffer_length(chunk))
1813
1841
  else:
1814
1842
  for chunk in it:
1815
1843
  yield chunk
@@ -1828,7 +1856,7 @@ async def progress_bytes_async_iter(
1828
1856
  update_progress: None | Callable = None
1829
1857
  close_progress: None | Callable = None
1830
1858
  if callable(it):
1831
- async def wrapiter(it) -> Buffer:
1859
+ async def wrapiter(it) -> AsyncIterator[Buffer]:
1832
1860
  while True:
1833
1861
  chunk = it()
1834
1862
  if isawaitable(chunk):
@@ -1856,7 +1884,7 @@ async def progress_bytes_async_iter(
1856
1884
  update_progress = ensure_async(update_progress)
1857
1885
  async for chunk in it:
1858
1886
  yield chunk
1859
- await update_progress(len(chunk))
1887
+ await update_progress(buffer_length(chunk))
1860
1888
  else:
1861
1889
  async for chunk in it:
1862
1890
  yield chunk
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-filewrap"
3
- version = "0.2.3.1"
3
+ version = "0.2.6"
4
4
  description = "Python file wrappers."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
@@ -28,7 +28,7 @@ include = [
28
28
  [tool.poetry.dependencies]
29
29
  python = "^3.10"
30
30
  python-asynctools = ">=0.0.5"
31
- python-property = ">=0.0.2"
31
+ python-property = ">=0.0.3"
32
32
 
33
33
  [build-system]
34
34
  requires = ["poetry-core"]