vercel-queue 0.7.0__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.
Files changed (43) hide show
  1. vercel/queue/__init__.py +8 -0
  2. vercel/queue/__main__.py +10 -0
  3. vercel/queue/_internal/__init__.py +1 -0
  4. vercel/queue/_internal/api_async.py +219 -0
  5. vercel/queue/_internal/api_common.py +104 -0
  6. vercel/queue/_internal/api_sync.py +137 -0
  7. vercel/queue/_internal/asgi.py +145 -0
  8. vercel/queue/_internal/asynctools.py +40 -0
  9. vercel/queue/_internal/cli.py +313 -0
  10. vercel/queue/_internal/client.py +1109 -0
  11. vercel/queue/_internal/client_sync.py +432 -0
  12. vercel/queue/_internal/config.py +160 -0
  13. vercel/queue/_internal/constants.py +50 -0
  14. vercel/queue/_internal/devserver.py +205 -0
  15. vercel/queue/_internal/embedded.py +1900 -0
  16. vercel/queue/_internal/errors.py +262 -0
  17. vercel/queue/_internal/http.py +634 -0
  18. vercel/queue/_internal/lease.py +1007 -0
  19. vercel/queue/_internal/log.py +143 -0
  20. vercel/queue/_internal/messages.py +122 -0
  21. vercel/queue/_internal/multipart.py +255 -0
  22. vercel/queue/_internal/names.py +101 -0
  23. vercel/queue/_internal/polling.py +200 -0
  24. vercel/queue/_internal/push.py +246 -0
  25. vercel/queue/_internal/response.py +111 -0
  26. vercel/queue/_internal/retry.py +86 -0
  27. vercel/queue/_internal/streams.py +313 -0
  28. vercel/queue/_internal/subscribers.py +1025 -0
  29. vercel/queue/_internal/transports.py +363 -0
  30. vercel/queue/_internal/types.py +300 -0
  31. vercel/queue/_internal/typeutils.py +203 -0
  32. vercel/queue/devserver.py +24 -0
  33. vercel/queue/embedded.py +50 -0
  34. vercel/queue/py.typed +1 -0
  35. vercel/queue/sync.py +8 -0
  36. vercel/queue/testing/__init__.py +14 -0
  37. vercel/queue/testing/pytest.py +42 -0
  38. vercel/queue/testing/state.py +32 -0
  39. vercel/queue/version.py +3 -0
  40. vercel_queue-0.7.0.dist-info/METADATA +681 -0
  41. vercel_queue-0.7.0.dist-info/RECORD +43 -0
  42. vercel_queue-0.7.0.dist-info/WHEEL +4 -0
  43. vercel_queue-0.7.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,313 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Concatenate, Generic, ParamSpec, TypeVar
4
+
5
+ import codecs
6
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator, Mapping
7
+ from functools import wraps
8
+
9
+ import anyio
10
+ from anyio.abc import ByteReceiveStream
11
+ from anyio.streams.buffered import BufferedByteReceiveStream
12
+
13
+ from .asynctools import iter_async_iterator
14
+
15
+ _DEFAULT_READ_SIZE = 64 * 1024
16
+ _DEFAULT_DELIMITER_LIMIT = 64 * 1024
17
+
18
+ _P = ParamSpec("_P")
19
+ _R = TypeVar("_R")
20
+ _T = TypeVar("_T")
21
+
22
+
23
+ class AsyncIteratorByteReceiveStream(ByteReceiveStream):
24
+ """Adapt an async byte iterator to AnyIO's byte receive stream protocol."""
25
+
26
+ def __init__(self, stream: AsyncIterator[bytes]) -> None:
27
+ self._stream = stream
28
+ self._buffer = bytearray()
29
+ self._closed = False
30
+
31
+ @property
32
+ def extra_attributes(self) -> Mapping[Any, Any]:
33
+ return {}
34
+
35
+ async def receive(self, max_bytes: int = 65536) -> bytes:
36
+ if self._closed:
37
+ raise anyio.ClosedResourceError
38
+ if max_bytes < 1:
39
+ raise ValueError("max_bytes must be greater than zero")
40
+ if self._buffer:
41
+ chunk = bytes(self._buffer[:max_bytes])
42
+ del self._buffer[:max_bytes]
43
+ return chunk
44
+ try:
45
+ chunk = await anext(self._stream)
46
+ except StopAsyncIteration:
47
+ raise anyio.EndOfStream from None
48
+ if not chunk:
49
+ return await self.receive(max_bytes)
50
+ if len(chunk) > max_bytes:
51
+ self._buffer.extend(chunk[max_bytes:])
52
+ return chunk[:max_bytes]
53
+ return chunk
54
+
55
+ async def aclose(self) -> None:
56
+ self._closed = True
57
+ close = getattr(self._stream, "aclose", None)
58
+ if close is not None:
59
+ await close()
60
+
61
+
62
+ def buffered_byte_receive_stream(
63
+ stream: AsyncIterator[bytes],
64
+ ) -> BufferedByteReceiveStream:
65
+ return BufferedByteReceiveStream(AsyncIteratorByteReceiveStream(stream))
66
+
67
+
68
+ class _SyncStreamPayloadBase(Generic[_T]):
69
+ def __init__(self, stream: Iterator[_T]) -> None:
70
+ self._stream = stream
71
+ self._consumed = False
72
+
73
+ def __iter__(self) -> Iterator[_T]:
74
+ if self._consumed:
75
+ return iter(())
76
+ self._consumed = True
77
+ return self._stream
78
+
79
+ def finalize(self) -> None:
80
+ for _ in self._stream:
81
+ pass
82
+ self._consumed = True
83
+
84
+
85
+ class SyncStreamPayload(_SyncStreamPayloadBase[bytes]):
86
+ """One-shot synchronous byte stream for a message body.
87
+
88
+ Multipart receive responses expose part bodies as streams. The sync client
89
+ wraps the async body iterator so callers can process large payloads without
90
+ requiring the SDK to buffer them first.
91
+ """
92
+
93
+
94
+ class _ReadGuard:
95
+ def __init__(self) -> None:
96
+ self._reading = False
97
+
98
+
99
+ _S = TypeVar("_S", bound=_ReadGuard)
100
+
101
+
102
+ def _guarded_read(
103
+ func: Callable[Concatenate[_S, _P], Awaitable[_R]],
104
+ ) -> Callable[Concatenate[_S, _P], Awaitable[_R]]:
105
+ @wraps(func)
106
+ async def wrapper(self: _S, *args: _P.args, **kwargs: _P.kwargs) -> _R:
107
+ if self._reading:
108
+ raise anyio.BusyResourceError(f"reading from {type(self).__name__}")
109
+ self._reading = True
110
+ try:
111
+ return await func(self, *args, **kwargs)
112
+ finally:
113
+ self._reading = False
114
+
115
+ return wrapper
116
+
117
+
118
+ def _guarded_iter(
119
+ func: Callable[Concatenate[_S, _P], AsyncIterator[_T]],
120
+ ) -> Callable[Concatenate[_S, _P], AsyncIterator[_T]]:
121
+ @wraps(func)
122
+ async def wrapper(
123
+ self: _S,
124
+ *args: _P.args,
125
+ **kwargs: _P.kwargs,
126
+ ) -> AsyncIterator[_T]:
127
+ if self._reading:
128
+ raise anyio.BusyResourceError(f"reading from {type(self).__name__}")
129
+ self._reading = True
130
+ try:
131
+ async for item in func(self, *args, **kwargs):
132
+ yield item
133
+ finally:
134
+ self._reading = False
135
+
136
+ return wrapper
137
+
138
+
139
+ class AsyncStreamPayload(_ReadGuard):
140
+ """One-shot asynchronous byte stream for a message body.
141
+
142
+ Iteration and reader methods share a single cursor over the underlying
143
+ stream. Concurrent reads or iteration are unsupported.
144
+ """
145
+
146
+ def __init__(
147
+ self,
148
+ stream: AsyncIterator[bytes],
149
+ *,
150
+ on_close: Callable[[], Awaitable[None]] | None = None,
151
+ ) -> None:
152
+ super().__init__()
153
+ self._stream = buffered_byte_receive_stream(stream)
154
+ self._on_close = on_close
155
+ self._closed = False
156
+
157
+ def __aiter__(self) -> AsyncIterator[bytes]:
158
+ return self._iterate()
159
+
160
+ def to_sync(self) -> SyncStreamPayload:
161
+ return SyncStreamPayload(iter_async_iterator(aiter(self)))
162
+
163
+ @_guarded_read
164
+ async def read(self, n: int = -1) -> bytes:
165
+ try:
166
+ if n < 0:
167
+ return await self._read_all()
168
+ if n == 0:
169
+ return b""
170
+ return await self._stream.receive(n)
171
+ except anyio.EndOfStream:
172
+ return b""
173
+
174
+ @_guarded_read
175
+ async def readline(self) -> bytes:
176
+ try:
177
+ return await self._stream.receive_until(b"\n", _DEFAULT_DELIMITER_LIMIT) + b"\n"
178
+ except anyio.IncompleteRead:
179
+ partial = self._stream.buffer
180
+ self._stream.feed_data(b"")
181
+ await self._discard_buffer()
182
+ return partial
183
+
184
+ async def _read_all(self) -> bytes:
185
+ chunks: list[bytes] = []
186
+ while True:
187
+ try:
188
+ chunk = await self._stream.receive(_DEFAULT_READ_SIZE)
189
+ except anyio.EndOfStream:
190
+ break
191
+ chunks.append(chunk)
192
+ return b"".join(chunks)
193
+
194
+ @_guarded_read
195
+ async def readexactly(self, n: int) -> bytes:
196
+ if n < 0:
197
+ raise ValueError("readexactly size can not be less than zero")
198
+ return await self._stream.receive_exactly(n)
199
+
200
+ @_guarded_read
201
+ async def readuntil(self, separator: bytes = b"\n") -> bytes:
202
+ if not separator:
203
+ raise ValueError("Separator should be at least one-byte string")
204
+ return await self._stream.receive_until(separator, _DEFAULT_DELIMITER_LIMIT) + separator
205
+
206
+ @_guarded_read
207
+ async def afinalize(self) -> None:
208
+ if self._closed:
209
+ return
210
+ try:
211
+ while True:
212
+ try:
213
+ await self._stream.receive(_DEFAULT_READ_SIZE)
214
+ except anyio.EndOfStream:
215
+ break
216
+ finally:
217
+ self._closed = True
218
+ if self._on_close is not None:
219
+ await self._on_close()
220
+
221
+ @_guarded_iter
222
+ async def _iterate(self) -> AsyncIterator[bytes]:
223
+ while True:
224
+ try:
225
+ yield await self._stream.receive(_DEFAULT_READ_SIZE)
226
+ except anyio.EndOfStream:
227
+ break
228
+
229
+ async def _discard_buffer(self) -> None:
230
+ if not self._stream.buffer:
231
+ return
232
+ await self._stream.receive(len(self._stream.buffer))
233
+
234
+
235
+ class SyncTextStreamPayload(_SyncStreamPayloadBase[str]):
236
+ """One-shot synchronous text stream for a UTF-8 message body."""
237
+
238
+
239
+ class AsyncTextStreamPayload(_ReadGuard):
240
+ """One-shot asynchronous text stream for a UTF-8 message body."""
241
+
242
+ def __init__(
243
+ self,
244
+ stream: AsyncIterator[bytes] | AsyncStreamPayload,
245
+ *,
246
+ on_close: Callable[[], Awaitable[None]] | None = None,
247
+ ) -> None:
248
+ if isinstance(stream, AsyncStreamPayload):
249
+ self._stream = stream
250
+ else:
251
+ self._stream = AsyncStreamPayload(stream, on_close=on_close)
252
+ super().__init__()
253
+ self._decoder = codecs.getincrementaldecoder("utf-8")()
254
+ self._buffer = ""
255
+ self._eof = False
256
+ self._closed = False
257
+
258
+ def __aiter__(self) -> AsyncIterator[str]:
259
+ return self._iterate()
260
+
261
+ def to_sync(self) -> SyncTextStreamPayload:
262
+ return SyncTextStreamPayload(iter_async_iterator(aiter(self)))
263
+
264
+ @_guarded_read
265
+ async def read(self, n: int = -1) -> str:
266
+ if n == 0:
267
+ return ""
268
+ if n < 0:
269
+ return await self._read_all()
270
+ while len(self._buffer) < n:
271
+ text = await self._read_next_text()
272
+ if not text:
273
+ break
274
+ self._buffer += text
275
+ data = self._buffer[:n]
276
+ self._buffer = self._buffer[n:]
277
+ return data
278
+
279
+ async def afinalize(self) -> None:
280
+ if self._closed:
281
+ return
282
+ try:
283
+ await self._stream.afinalize()
284
+ finally:
285
+ self._closed = True
286
+
287
+ @_guarded_iter
288
+ async def _iterate(self) -> AsyncIterator[str]:
289
+ if self._buffer:
290
+ yield self._read_buffered_text()
291
+ while text := await self._read_next_text():
292
+ yield text
293
+
294
+ async def _read_all(self) -> str:
295
+ chunks = [self._read_buffered_text()] if self._buffer else []
296
+ while text := await self._read_next_text():
297
+ chunks.append(text)
298
+ return "".join(chunks)
299
+
300
+ def _read_buffered_text(self) -> str:
301
+ text = self._buffer
302
+ self._buffer = ""
303
+ return text
304
+
305
+ async def _read_next_text(self) -> str:
306
+ if self._eof:
307
+ return ""
308
+ while chunk := await self._stream.read(_DEFAULT_READ_SIZE):
309
+ text = self._decoder.decode(chunk)
310
+ if text:
311
+ return text
312
+ self._eof = True
313
+ return self._decoder.decode(b"", final=True)