python-filewrap 0.0.7.2__tar.gz → 0.1__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.0.7.2
3
+ Version: 0.1
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
@@ -2,9 +2,10 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 0, 7)
5
+ __version__ = (0, 1)
6
6
  __all__ = [
7
- "SupportsRead", "SupportsWrite", "SupportsSeek",
7
+ "Buffer", "SupportsRead", "SupportsReadinto",
8
+ "SupportsWrite", "SupportsSeek",
8
9
  "bio_chunk_iter", "bio_chunk_async_iter",
9
10
  "bio_skip_iter", "bio_skip_async_iter",
10
11
  "bytes_iter_skip", "bytes_async_iter_skip",
@@ -25,7 +26,39 @@ from typing import runtime_checkable, Any, Protocol, TypeVar
25
26
  try:
26
27
  from collections.abc import Buffer # type: ignore
27
28
  except ImportError:
28
- Buffer = Any
29
+ from abc import ABC, abstractmethod
30
+ from array import array
31
+
32
+ def _check_methods(C, *methods):
33
+ mro = C.__mro__
34
+ for method in methods:
35
+ for B in mro:
36
+ if method in B.__dict__:
37
+ if B.__dict__[method] is None:
38
+ return NotImplemented
39
+ break
40
+ else:
41
+ return NotImplemented
42
+ return True
43
+
44
+ class Buffer(ABC): # type: ignore
45
+ __slots__ = ()
46
+
47
+ @abstractmethod
48
+ def __buffer__(self, flags: int, /) -> memoryview:
49
+ raise NotImplementedError
50
+
51
+ @classmethod
52
+ def __subclasshook__(cls, C):
53
+ if cls is Buffer:
54
+ return _check_methods(C, "__buffer__")
55
+ return NotImplemented
56
+
57
+ Buffer.register(bytes)
58
+ Buffer.register(bytearray)
59
+ Buffer.register(memoryview)
60
+ Buffer.register(array)
61
+
29
62
 
30
63
  from asynctools import async_chain, ensure_async, ensure_aiter
31
64
 
@@ -39,13 +72,18 @@ class SupportsRead(Protocol[_T_co]):
39
72
  def read(self, /, __length: int = ...) -> _T_co: ...
40
73
 
41
74
 
75
+ @runtime_checkable
76
+ class SupportsReadinto(Protocol):
77
+ def readinto(self, /, buf: Buffer = ...) -> int: ...
78
+
79
+
42
80
  @runtime_checkable
43
81
  class SupportsWrite(Protocol[_T_contra]):
44
82
  def write(self, /, __s: _T_contra) -> object: ...
45
83
 
46
84
 
47
85
  @runtime_checkable
48
- class SupportsSeek(Protocol[_T_contra]):
86
+ class SupportsSeek(Protocol):
49
87
  def seek(self, /, __offset: int, __whence: int = 0) -> int: ...
50
88
 
51
89
 
@@ -54,30 +92,60 @@ def bio_chunk_iter(
54
92
  /,
55
93
  size: int = -1,
56
94
  chunksize: int = COPY_BUFSIZE,
95
+ can_buffer: bool = False,
57
96
  callback: None | Callable[[int], Any] = None,
58
97
  ) -> Iterator[Buffer]:
98
+ use_readinto = False
59
99
  if callable(bio):
60
100
  read = bio
101
+ elif can_buffer and hasattr(bio, "readinto"):
102
+ readinto = bio.readinto
103
+ use_readinto = True
61
104
  else:
62
105
  read = bio.read
63
106
  if not callable(callback):
64
107
  callback = None
65
- if size > 0:
66
- while size:
67
- readsize = min(chunksize, size)
68
- chunk = read(readsize)
69
- length = len(chunk)
70
- if callback:
71
- callback(length)
72
- yield chunk
73
- if length < readsize:
74
- break
75
- size -= readsize
76
- elif size < 0:
77
- while (chunk := read(chunksize)):
78
- if callback:
79
- callback(len(chunk))
80
- yield chunk
108
+ if use_readinto:
109
+ buf = bytearray(chunksize)
110
+ if size > 0:
111
+ while size:
112
+ if size < chunksize:
113
+ del buf[size:]
114
+ length = readinto(buf)
115
+ if callback:
116
+ callback(length)
117
+ if length < len(buf):
118
+ del buf[length:]
119
+ yield buf
120
+ break
121
+ yield buf
122
+ size -= length
123
+ else:
124
+ while (length := readinto(buf)):
125
+ if callback:
126
+ callback(length)
127
+ if length < chunksize:
128
+ del buf[length:]
129
+ yield buf
130
+ break
131
+ yield buf
132
+ else:
133
+ if size > 0:
134
+ while size:
135
+ readsize = min(chunksize, size)
136
+ chunk = read(readsize)
137
+ length = len(chunk)
138
+ if callback:
139
+ callback(length)
140
+ yield chunk
141
+ if length < readsize:
142
+ break
143
+ size -= length
144
+ elif size < 0:
145
+ while (chunk := read(chunksize)):
146
+ if callback:
147
+ callback(len(chunk))
148
+ yield chunk
81
149
 
82
150
 
83
151
  async def bio_chunk_async_iter(
@@ -85,29 +153,59 @@ async def bio_chunk_async_iter(
85
153
  /,
86
154
  size: int = -1,
87
155
  chunksize: int = COPY_BUFSIZE,
156
+ can_buffer: bool = False,
88
157
  callback: None | Callable[[int], Any] = None,
89
158
  ) -> AsyncIterator[Buffer]:
159
+ use_readinto = False
90
160
  if callable(bio):
91
161
  read = ensure_async(bio)
162
+ elif can_buffer and hasattr(bio, "readinto"):
163
+ readinto = ensure_async(bio.readinto)
164
+ use_readinto = True
92
165
  else:
93
166
  read = ensure_async(bio.read)
94
167
  callback = ensure_async(callback) if callable(callback) else None
95
- if size > 0:
96
- while size:
97
- readsize = min(chunksize, size)
98
- chunk = await read(readsize)
99
- length = len(chunk)
100
- if callback:
101
- await callback(length)
102
- yield chunk
103
- if length < readsize:
104
- break
105
- size -= readsize
106
- elif size < 0:
107
- while (chunk := (await read(chunksize))):
108
- if callback:
109
- await callback(len(chunk))
110
- yield chunk
168
+ if use_readinto:
169
+ buf = bytearray(chunksize)
170
+ if size > 0:
171
+ while size:
172
+ if size < chunksize:
173
+ del buf[size:]
174
+ length = await readinto(buf)
175
+ if callback:
176
+ await callback(length)
177
+ if length < len(buf):
178
+ del buf[length:]
179
+ yield buf
180
+ break
181
+ yield buf
182
+ size -= length
183
+ else:
184
+ while (length := (await readinto(buf))):
185
+ if callback:
186
+ await callback(length)
187
+ if length < chunksize:
188
+ del buf[length:]
189
+ yield buf
190
+ break
191
+ yield buf
192
+ else:
193
+ if size > 0:
194
+ while size:
195
+ readsize = min(chunksize, size)
196
+ chunk = await read(readsize)
197
+ length = len(chunk)
198
+ if callback:
199
+ await callback(length)
200
+ yield chunk
201
+ if length < readsize:
202
+ break
203
+ size -= readsize
204
+ elif size < 0:
205
+ while (chunk := (await read(chunksize))):
206
+ if callback:
207
+ await callback(len(chunk))
208
+ yield chunk
111
209
 
112
210
 
113
211
  def bio_skip_iter(
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-filewrap"
3
- version = "0.0.7.2"
3
+ version = "0.1"
4
4
  description = "Python file wrappers."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
File without changes