vercel-queue 0.8.0__tar.gz → 0.8.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.
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/PKG-INFO +6 -6
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/README.md +1 -1
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/devserver.py +71 -11
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/version.py +1 -1
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/.gitignore +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/LICENSE +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/_vercel_hatch_build.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/hatch_build.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/pyproject.toml +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/__init__.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/__main__.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/__init__.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/api_async.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/api_common.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/api_sync.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/asgi.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/asynctools.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/cli.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/client.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/client_sync.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/config.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/constants.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/embedded.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/errors.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/http.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/lease.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/log.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/messages.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/multipart.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/names.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/polling.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/push.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/response.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/retry.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/streams.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/subscribers.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/transports.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/_internal/types.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/devserver.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/embedded.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/py.typed +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/sync.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/testing/__init__.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/testing/pytest.py +0 -0
- {vercel_queue-0.8.0 → vercel_queue-0.8.1}/vercel/queue/testing/state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: vercel-queue
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Vercel Queue client for Python
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -9,9 +9,9 @@ Requires-Dist: anyio>=4.0.0
|
|
|
9
9
|
Requires-Dist: httpx[http2]>=0.27.0
|
|
10
10
|
Requires-Dist: python-multipart>=0.0.20
|
|
11
11
|
Requires-Dist: typing-extensions>=4.0.0
|
|
12
|
-
Requires-Dist: vercel-headers>=0.7.
|
|
13
|
-
Requires-Dist: vercel-internal-core<0.2.0,>=0.1.
|
|
14
|
-
Requires-Dist: vercel-oidc>=0.8.
|
|
12
|
+
Requires-Dist: vercel-headers>=0.7.2
|
|
13
|
+
Requires-Dist: vercel-internal-core<0.2.0,>=0.1.3
|
|
14
|
+
Requires-Dist: vercel-oidc>=0.8.1
|
|
15
15
|
Provides-Extra: devserver
|
|
16
16
|
Requires-Dist: uvicorn; extra == 'devserver'
|
|
17
17
|
Provides-Extra: trio
|
|
@@ -27,7 +27,7 @@ designed for seamless integration with Vercel deployments.
|
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
29
|
|
|
30
|
-
- **Simple API**: `send`, `subscribe`, and `
|
|
30
|
+
- **Simple API**: `send`, `subscribe`, and `poll` cover standard workflows.
|
|
31
31
|
- **Automatic Triggering on Vercel**: Vercel invokes your function when messages are ready.
|
|
32
32
|
- **Works Anywhere**: `send`, decorated subscribers, and manual `poll` loops work on Vercel, self-hosted workers, and locally.
|
|
33
33
|
- **Sync and Async Clients**: Prefer async for applications, use sync for scripts and blocking workers.
|
|
@@ -5,7 +5,7 @@ designed for seamless integration with Vercel deployments.
|
|
|
5
5
|
|
|
6
6
|
## Features
|
|
7
7
|
|
|
8
|
-
- **Simple API**: `send`, `subscribe`, and `
|
|
8
|
+
- **Simple API**: `send`, `subscribe`, and `poll` cover standard workflows.
|
|
9
9
|
- **Automatic Triggering on Vercel**: Vercel invokes your function when messages are ready.
|
|
10
10
|
- **Works Anywhere**: `send`, decorated subscribers, and manual `poll` loops work on Vercel, self-hosted workers, and locally.
|
|
11
11
|
- **Sync and Async Clients**: Prefer async for applications, use sync for scripts and blocking workers.
|
|
@@ -15,6 +15,9 @@ import time
|
|
|
15
15
|
from collections.abc import Callable, Iterator, Mapping
|
|
16
16
|
from dataclasses import dataclass
|
|
17
17
|
|
|
18
|
+
import anyio
|
|
19
|
+
from anyio import from_thread
|
|
20
|
+
|
|
18
21
|
from ..embedded import create_embedded_queue_app
|
|
19
22
|
from .asgi import QueueClientAsgiApp, asgi_app
|
|
20
23
|
from .client import QueueClient
|
|
@@ -23,6 +26,9 @@ from .embedded import EmbeddedQueueDevServer
|
|
|
23
26
|
from .http import AsyncHttpClientFactory
|
|
24
27
|
from .types import Duration
|
|
25
28
|
|
|
29
|
+
_GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS = 5.0
|
|
30
|
+
_SERVER_STOP_TIMEOUT_MARGIN_SECONDS = 1.0
|
|
31
|
+
|
|
26
32
|
|
|
27
33
|
@dataclass(frozen=True, kw_only=True)
|
|
28
34
|
class QueueClientAsgiDevServer:
|
|
@@ -55,8 +61,9 @@ def embedded_queue_dev_server(
|
|
|
55
61
|
log_level="warning",
|
|
56
62
|
lifespan="off",
|
|
57
63
|
ws="none",
|
|
64
|
+
timeout_graceful_shutdown=_GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS,
|
|
58
65
|
)
|
|
59
|
-
server = uvicorn
|
|
66
|
+
server = _cancellable_server(uvicorn, config)
|
|
60
67
|
thread = threading.Thread(target=_profiled_server_run(server, profile), daemon=True)
|
|
61
68
|
thread.start()
|
|
62
69
|
_wait_for_server(server)
|
|
@@ -69,11 +76,15 @@ def embedded_queue_dev_server(
|
|
|
69
76
|
_thread=thread,
|
|
70
77
|
)
|
|
71
78
|
finally:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
try:
|
|
80
|
+
_stop_server(
|
|
81
|
+
server,
|
|
82
|
+
thread,
|
|
83
|
+
"embedded queue dev server",
|
|
84
|
+
timeout=_GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS,
|
|
85
|
+
)
|
|
86
|
+
finally:
|
|
87
|
+
app.state.close()
|
|
77
88
|
|
|
78
89
|
|
|
79
90
|
@contextlib.contextmanager
|
|
@@ -110,8 +121,9 @@ def queue_client_asgi_dev_server( # noqa: PLR0913
|
|
|
110
121
|
log_level="warning",
|
|
111
122
|
lifespan="on",
|
|
112
123
|
ws="none",
|
|
124
|
+
timeout_graceful_shutdown=_GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS,
|
|
113
125
|
)
|
|
114
|
-
server = uvicorn
|
|
126
|
+
server = _cancellable_server(uvicorn, config)
|
|
115
127
|
thread = threading.Thread(target=_profiled_server_run(server, profile), daemon=True)
|
|
116
128
|
thread.start()
|
|
117
129
|
_wait_for_server(server, "queue client ASGI dev server")
|
|
@@ -123,10 +135,12 @@ def queue_client_asgi_dev_server( # noqa: PLR0913
|
|
|
123
135
|
_thread=thread,
|
|
124
136
|
)
|
|
125
137
|
finally:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
_stop_server(
|
|
139
|
+
server,
|
|
140
|
+
thread,
|
|
141
|
+
"queue client ASGI dev server",
|
|
142
|
+
timeout=_GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS,
|
|
143
|
+
)
|
|
130
144
|
|
|
131
145
|
|
|
132
146
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -181,6 +195,52 @@ def _wait_for_server(server: Any, name: str = "embedded queue dev server") -> No
|
|
|
181
195
|
raise RuntimeError(f"{name} did not start")
|
|
182
196
|
|
|
183
197
|
|
|
198
|
+
def _stop_server(
|
|
199
|
+
server: Any,
|
|
200
|
+
thread: threading.Thread,
|
|
201
|
+
name: str,
|
|
202
|
+
*,
|
|
203
|
+
timeout: float,
|
|
204
|
+
) -> None:
|
|
205
|
+
server.should_exit = True
|
|
206
|
+
thread.join(timeout=timeout + _SERVER_STOP_TIMEOUT_MARGIN_SECONDS)
|
|
207
|
+
if thread.is_alive():
|
|
208
|
+
server.force_exit = True
|
|
209
|
+
server.cancel()
|
|
210
|
+
thread.join(timeout=timeout)
|
|
211
|
+
if thread.is_alive():
|
|
212
|
+
raise RuntimeError(f"{name} did not stop")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _cancellable_server(uvicorn: Any, config: Any) -> Any:
|
|
216
|
+
class CancellableServer(uvicorn.Server):
|
|
217
|
+
_cancel_scope: anyio.CancelScope | None = None
|
|
218
|
+
_portal: from_thread.BlockingPortal | None = None
|
|
219
|
+
|
|
220
|
+
def run(self, sockets: list[Any] | None = None) -> None:
|
|
221
|
+
backend_options: dict[str, Any] = {}
|
|
222
|
+
get_loop_factory = getattr(self.config, "get_loop_factory", None)
|
|
223
|
+
if get_loop_factory is not None:
|
|
224
|
+
backend_options["loop_factory"] = get_loop_factory()
|
|
225
|
+
else:
|
|
226
|
+
self.config.setup_event_loop()
|
|
227
|
+
anyio.run(self._run, sockets, backend_options=backend_options)
|
|
228
|
+
|
|
229
|
+
async def _run(self, sockets: list[Any] | None) -> None:
|
|
230
|
+
async with from_thread.BlockingPortal() as self._portal:
|
|
231
|
+
with anyio.CancelScope() as self._cancel_scope:
|
|
232
|
+
await self.serve(sockets=sockets)
|
|
233
|
+
|
|
234
|
+
def cancel(self) -> None:
|
|
235
|
+
if self._portal is not None and self._cancel_scope is not None:
|
|
236
|
+
try:
|
|
237
|
+
self._portal.start_task_soon(self._cancel_scope.cancel)
|
|
238
|
+
except RuntimeError:
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
return CancellableServer(config)
|
|
242
|
+
|
|
243
|
+
|
|
184
244
|
def _server_port(server: Any) -> int:
|
|
185
245
|
for asyncio_server in server.servers:
|
|
186
246
|
sockets = asyncio_server.sockets or ()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|