python-filewrap 0.2.3.1__tar.gz → 0.2.5__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.5
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
@@ -22,7 +22,7 @@ Classifier: Topic :: Software Development
22
22
  Classifier: Topic :: Software Development :: Libraries
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Dist: python-asynctools (>=0.0.5)
25
- Requires-Dist: python-property (>=0.0.2)
25
+ Requires-Dist: python-property (>=0.0.3)
26
26
  Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-filewrap
27
27
  Description-Content-Type: text/markdown
28
28
 
@@ -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, 5)
10
11
  __all__ = [
11
12
  "Buffer", "SupportsRead", "SupportsReadinto", "SupportsWrite", "SupportsSeek",
12
13
  "AsyncBufferedReader", "AsyncTextIOWrapper",
@@ -22,7 +23,7 @@ __all__ = [
22
23
  ]
23
24
 
24
25
  from asyncio import to_thread, Lock as AsyncLock
25
- from collections.abc import Awaitable, AsyncIterable, AsyncIterator, Callable, Iterable, Iterator
26
+ from collections.abc import Awaitable, AsyncIterable, AsyncIterator, Callable, Iterable, Iterator, Sized
26
27
  from functools import update_wrapper
27
28
  from io import BufferedIOBase, BufferedReader, BytesIO, RawIOBase, TextIOWrapper
28
29
  from inspect import isawaitable, iscoroutinefunction, isasyncgen, isgenerator
@@ -51,7 +52,7 @@ except ImportError:
51
52
  Buffer.register(array)
52
53
 
53
54
  from asynctools import async_chain, ensure_async, ensure_aiter, run_async
54
- from property import staticproperty
55
+ from property import funcproperty
55
56
 
56
57
 
57
58
  Args = ParamSpec("Args")
@@ -629,7 +630,9 @@ class AsyncTextIOWrapper(TextIOWrapper):
629
630
  data = await read(-1)
630
631
  else:
631
632
  data = await read(size)
632
- if size < 0 or len(data) < size:
633
+ if not isinstance(data, Sized):
634
+ data = memoryview(data)
635
+ if size < 0 or len(cast(Sized, data)) < size:
633
636
  text = str(data, encoding, errors)
634
637
  if newline is None:
635
638
  text = CRE_NOT_UNIX_NEWLINES_sub("\n", text)
@@ -644,8 +647,10 @@ class AsyncTextIOWrapper(TextIOWrapper):
644
647
 
645
648
  ls_parts: list[str] = []
646
649
  add_part = ls_parts.append
647
- cache = data
648
- while size and len(data) == size:
650
+ if not isinstance(data, Sized):
651
+ data = memoryview(data)
652
+ cache = bytes(data)
653
+ while size and len(cast(Sized, data)) == size:
649
654
  while cache:
650
655
  try:
651
656
  size -= process_part(cache)
@@ -684,6 +689,8 @@ class AsyncTextIOWrapper(TextIOWrapper):
684
689
  size -= process_part(cache[start:stop], errors)
685
690
  cache = cache[stop:]
686
691
  data = await read(size)
692
+ if not isinstance(data, Sized):
693
+ data = memoryview(data)
687
694
  cache += data
688
695
  if cache:
689
696
  process_part(cache, errors)
@@ -730,12 +737,15 @@ class AsyncTextIOWrapper(TextIOWrapper):
730
737
  break
731
738
  elif buf.endswith(crb):
732
739
  peek_maybe_lfb = await read(lfb_len)
740
+ if not isinstance(peek_maybe_lfb, Sized):
741
+ peek_maybe_lfb = memoryview(peek_maybe_lfb)
733
742
  if peek_maybe_lfb == lfb:
734
743
  buf += lfb
735
744
  elif peek_maybe_lfb:
736
745
  # TODO: 这是一个提前量,未必需要立即往回 seek,因为转换为 str 后可能尾部不是 \r(因为可以和前面的符号结合),所以这个可能可以被复用,如果需要优化,可以在程序结束时的 finally 部分最终执行 seek(可能最终字符被消耗所以不需要 seek)
737
- await seek(-len(peek_maybe_lfb), 1)
738
- if len(peek_maybe_lfb) < lfb_len:
746
+ o = len(cast(Sized, peek_maybe_lfb))
747
+ await seek(-o, 1)
748
+ if o < lfb_len:
739
749
  reach_end = True
740
750
  break
741
751
  else:
@@ -839,11 +849,14 @@ class AsyncTextIOWrapper(TextIOWrapper):
839
849
  break
840
850
  elif buf.endswith(crb):
841
851
  peek_maybe_lfb = await read(lfb_len)
852
+ if not isinstance(peek_maybe_lfb, Sized):
853
+ peek_maybe_lfb = memoryview(peek_maybe_lfb)
842
854
  if peek_maybe_lfb == lfb:
843
855
  buf += lfb
844
856
  elif peek_maybe_lfb:
845
- await seek(-len(peek_maybe_lfb), 1)
846
- if len(peek_maybe_lfb) < lfb_len:
857
+ o = len(cast(Sized, peek_maybe_lfb))
858
+ await seek(-o, 1)
859
+ if o < lfb_len:
847
860
  reach_end = True
848
861
  break
849
862
  else:
@@ -951,13 +964,13 @@ class AsyncTextIOWrapper(TextIOWrapper):
951
964
  self.newline = newline
952
965
 
953
966
  async def seek(self, target: int, whence: int = 0, /) -> int: # type: ignore
954
- return await ensure_async(self.buffer.seek, threaded=True)(target, whence)
967
+ return await ensure_async(getattr(self.buffer, "seek"), threaded=True)(target, whence)
955
968
 
956
969
  def tell(self, /) -> int:
957
- return self.buffer.tell()
970
+ return getattr(self.buffer, "tell")()
958
971
 
959
972
  async def truncate(self, pos: None | int = None, /) -> int: # type: ignore
960
- return await ensure_async(self.buffer.truncate, threaded=True)(pos)
973
+ return await ensure_async(getattr(self.buffer, "truncate"), threaded=True)(pos)
961
974
 
962
975
  async def write(self, text: str, /) -> int: # type: ignore
963
976
  match self.newline:
@@ -1330,7 +1343,8 @@ def bytes_iter_skip(
1330
1343
  return it
1331
1344
  if not callable(callback):
1332
1345
  callback = None
1333
- for m in map(memoryview, it):
1346
+ m: memoryview
1347
+ for m in map(memoryview, it): # type: ignore
1334
1348
  l = len(m)
1335
1349
  if callback:
1336
1350
  callback(min(l, size))
@@ -1516,7 +1530,7 @@ def bytes_iter_to_reader(
1516
1530
  "__next__": staticmethod(__next__),
1517
1531
  "__repr__": staticmethod(lambda: reprs),
1518
1532
  "close": staticmethod(close),
1519
- "closed": staticproperty(lambda: at_end),
1533
+ "closed": funcproperty(staticmethod(lambda: at_end)),
1520
1534
  "peek": staticmethod(peek),
1521
1535
  "read": staticmethod(read),
1522
1536
  "readinto": staticmethod(readinto),
@@ -1697,7 +1711,7 @@ def bytes_iter_to_async_reader(
1697
1711
  "__repr__": staticmethod(lambda: reprs),
1698
1712
  "close": staticmethod(close),
1699
1713
  "aclose": staticmethod(aclose),
1700
- "closed": staticproperty(lambda: at_end),
1714
+ "closed": funcproperty(staticmethod(lambda: at_end)),
1701
1715
  "peek": staticmethod(peek),
1702
1716
  "read": staticmethod(read),
1703
1717
  "readinto": staticmethod(readinto),
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-filewrap"
3
- version = "0.2.3.1"
3
+ version = "0.2.5"
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"]