vercel-queue-bundle 0.7.2__tar.gz → 0.7.3__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_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/PKG-INFO +46 -47
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/README.md +43 -45
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/_vercel_hatch_build.py +38 -3
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/pyproject.toml +7 -1
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/subscribers.py +9 -8
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/transports.py +11 -10
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/version.py +1 -1
- vercel_queue_bundle-0.7.2/vercel/queue/_internal/typeutils.py +0 -203
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/LICENSE +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/hatch_build.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/__init__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/__main__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/__init__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/api_async.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/api_common.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/api_sync.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/asgi.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/asynctools.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/cli.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/client.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/client_sync.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/config.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/constants.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/devserver.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/embedded.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/errors.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/http.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/lease.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/log.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/messages.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/multipart.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/names.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/polling.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/push.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/response.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/retry.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/streams.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/types.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/LICENSE.python-multipart.txt +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/__init__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/__init__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/decoders.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/exceptions.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/multipart.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/python_multipart/__init__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/python_multipart/decoders.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/python_multipart/exceptions.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/python_multipart/multipart.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/devserver.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/embedded.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/py.typed +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/sync.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/testing/__init__.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/testing/pytest.py +0 -0
- {vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/testing/state.py +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vercel-queue-bundle
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Vercel Queue client for Python
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
License-File: vercel/queue/_vendor/LICENSE.python-multipart.txt
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Requires-Dist: vercel-headers-bundle>=0.7.1
|
|
10
|
+
Requires-Dist: vercel-internal-core-bundle<0.2.0,>=0.1.2
|
|
10
11
|
Requires-Dist: vercel-internal-shared-vendored-deps>=0.1.1
|
|
11
|
-
Requires-Dist: vercel-oidc-bundle>=0.
|
|
12
|
+
Requires-Dist: vercel-oidc-bundle>=0.8.0
|
|
12
13
|
Provides-Extra: devserver
|
|
13
14
|
Requires-Dist: uvicorn; extra == 'devserver'
|
|
14
15
|
Provides-Extra: trio
|
|
@@ -69,35 +70,29 @@ from vercel.queue import send
|
|
|
69
70
|
message_id = await send("my-topic", {"message": "Hello world"})
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
**3. Handle incoming messages
|
|
73
|
+
**3. Handle incoming messages in a subscriber module:**
|
|
73
74
|
|
|
74
75
|
```python
|
|
75
|
-
#
|
|
76
|
-
from vercel.queue import
|
|
76
|
+
# worker.py
|
|
77
|
+
from vercel.queue import subscribe
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
@subscribe(topic="my-topic"
|
|
80
|
+
@subscribe(topic="my-topic")
|
|
80
81
|
async def process_message(message):
|
|
81
82
|
print("Processing:", message)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# An ASGI app instance that converts incoming message callbacks
|
|
85
|
-
# sent by Vercel Queues and routes them to handlers.
|
|
86
|
-
app = asgi_app()
|
|
87
83
|
```
|
|
88
84
|
|
|
89
|
-
**4.
|
|
85
|
+
**4. Declare the subscriber in `pyproject.toml`:**
|
|
90
86
|
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
"api/queue.py": {
|
|
95
|
-
"experimentalTriggers": [{ "type": "queue/v2beta", "topic": "my-topic" }]
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
87
|
+
```toml
|
|
88
|
+
[[tool.vercel.subscribers]]
|
|
89
|
+
entrypoint = "worker:process_message"
|
|
99
90
|
```
|
|
100
91
|
|
|
92
|
+
The Vercel build introspects the entrypoint module's registered
|
|
93
|
+
subscriptions and compiles it into a queue-triggered function. No
|
|
94
|
+
`vercel.json` trigger configuration is needed.
|
|
95
|
+
|
|
101
96
|
**5. Deploy:**
|
|
102
97
|
|
|
103
98
|
```bash
|
|
@@ -165,42 +160,46 @@ and running polling loops on other infrastructure.
|
|
|
165
160
|
|
|
166
161
|
### Auto-scaled push-mode on Vercel
|
|
167
162
|
|
|
168
|
-
The recommended way of deploying queue subscribers is to
|
|
169
|
-
|
|
170
|
-
|
|
163
|
+
The recommended way of deploying queue subscribers is to declare them in
|
|
164
|
+
`pyproject.toml` with `[[tool.vercel.subscribers]]`. Each entry points at a
|
|
165
|
+
module that registers subscriptions with `@subscribe`:
|
|
171
166
|
|
|
172
167
|
```python
|
|
173
|
-
#
|
|
174
|
-
from vercel.queue import
|
|
168
|
+
# worker.py
|
|
169
|
+
from vercel.queue import subscribe
|
|
175
170
|
|
|
176
171
|
|
|
177
|
-
@subscribe(topic="orders",
|
|
172
|
+
@subscribe(topic="orders", retry_after=60)
|
|
178
173
|
async def handle_order(message):
|
|
179
174
|
print("Processing:", message)
|
|
175
|
+
```
|
|
180
176
|
|
|
177
|
+
**pyproject.toml**:
|
|
181
178
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
```toml
|
|
180
|
+
[[tool.vercel.subscribers]]
|
|
181
|
+
entrypoint = "worker:handle_order"
|
|
185
182
|
```
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
184
|
+
At build time, Vercel imports the entrypoint module, introspects every
|
|
185
|
+
subscription it registers, and compiles the subscriber into its own
|
|
186
|
+
queue-triggered function. Delivery tuning lives on the `@subscribe`
|
|
187
|
+
decorator (`retry_after`, `initial_delay`, `max_concurrency`,
|
|
188
|
+
`max_attempts`, `consumer_group`), so no `vercel.json` configuration is
|
|
189
|
+
needed.
|
|
190
|
+
|
|
191
|
+
A subscriber consumes every subscription registered by its entrypoint
|
|
192
|
+
module. Add an optional `topics` filter to split subscriptions across
|
|
193
|
+
several subscriber functions:
|
|
194
|
+
|
|
195
|
+
```toml
|
|
196
|
+
[[tool.vercel.subscribers]]
|
|
197
|
+
entrypoint = "worker:handle_order"
|
|
198
|
+
topics = ["orders"]
|
|
199
|
+
|
|
200
|
+
[[tool.vercel.subscribers]]
|
|
201
|
+
entrypoint = "worker:handle_refund"
|
|
202
|
+
topics = ["refunds"]
|
|
204
203
|
```
|
|
205
204
|
|
|
206
205
|
### Automatic Polling Loop
|
|
@@ -287,7 +286,7 @@ polling, because it can distribute messages across regions unpredictably.
|
|
|
287
286
|
## Retry and Backoff
|
|
288
287
|
|
|
289
288
|
When a topic handler raises, the message is not acknowledged and becomes available for redelivery
|
|
290
|
-
after the `
|
|
289
|
+
after the `retry_after` interval configured on the subscriber. Retries continue until the
|
|
291
290
|
handler succeeds or the message expires.
|
|
292
291
|
|
|
293
292
|
For finer control over retry timing, raise `RetryAfter` from a subscriber:
|
|
@@ -50,35 +50,29 @@ from vercel.queue import send
|
|
|
50
50
|
message_id = await send("my-topic", {"message": "Hello world"})
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
**3. Handle incoming messages
|
|
53
|
+
**3. Handle incoming messages in a subscriber module:**
|
|
54
54
|
|
|
55
55
|
```python
|
|
56
|
-
#
|
|
57
|
-
from vercel.queue import
|
|
56
|
+
# worker.py
|
|
57
|
+
from vercel.queue import subscribe
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
@subscribe(topic="my-topic"
|
|
60
|
+
@subscribe(topic="my-topic")
|
|
61
61
|
async def process_message(message):
|
|
62
62
|
print("Processing:", message)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# An ASGI app instance that converts incoming message callbacks
|
|
66
|
-
# sent by Vercel Queues and routes them to handlers.
|
|
67
|
-
app = asgi_app()
|
|
68
63
|
```
|
|
69
64
|
|
|
70
|
-
**4.
|
|
65
|
+
**4. Declare the subscriber in `pyproject.toml`:**
|
|
71
66
|
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"api/queue.py": {
|
|
76
|
-
"experimentalTriggers": [{ "type": "queue/v2beta", "topic": "my-topic" }]
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
67
|
+
```toml
|
|
68
|
+
[[tool.vercel.subscribers]]
|
|
69
|
+
entrypoint = "worker:process_message"
|
|
80
70
|
```
|
|
81
71
|
|
|
72
|
+
The Vercel build introspects the entrypoint module's registered
|
|
73
|
+
subscriptions and compiles it into a queue-triggered function. No
|
|
74
|
+
`vercel.json` trigger configuration is needed.
|
|
75
|
+
|
|
82
76
|
**5. Deploy:**
|
|
83
77
|
|
|
84
78
|
```bash
|
|
@@ -146,42 +140,46 @@ and running polling loops on other infrastructure.
|
|
|
146
140
|
|
|
147
141
|
### Auto-scaled push-mode on Vercel
|
|
148
142
|
|
|
149
|
-
The recommended way of deploying queue subscribers is to
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
The recommended way of deploying queue subscribers is to declare them in
|
|
144
|
+
`pyproject.toml` with `[[tool.vercel.subscribers]]`. Each entry points at a
|
|
145
|
+
module that registers subscriptions with `@subscribe`:
|
|
152
146
|
|
|
153
147
|
```python
|
|
154
|
-
#
|
|
155
|
-
from vercel.queue import
|
|
148
|
+
# worker.py
|
|
149
|
+
from vercel.queue import subscribe
|
|
156
150
|
|
|
157
151
|
|
|
158
|
-
@subscribe(topic="orders",
|
|
152
|
+
@subscribe(topic="orders", retry_after=60)
|
|
159
153
|
async def handle_order(message):
|
|
160
154
|
print("Processing:", message)
|
|
155
|
+
```
|
|
161
156
|
|
|
157
|
+
**pyproject.toml**:
|
|
162
158
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
```toml
|
|
160
|
+
[[tool.vercel.subscribers]]
|
|
161
|
+
entrypoint = "worker:handle_order"
|
|
166
162
|
```
|
|
167
163
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
164
|
+
At build time, Vercel imports the entrypoint module, introspects every
|
|
165
|
+
subscription it registers, and compiles the subscriber into its own
|
|
166
|
+
queue-triggered function. Delivery tuning lives on the `@subscribe`
|
|
167
|
+
decorator (`retry_after`, `initial_delay`, `max_concurrency`,
|
|
168
|
+
`max_attempts`, `consumer_group`), so no `vercel.json` configuration is
|
|
169
|
+
needed.
|
|
170
|
+
|
|
171
|
+
A subscriber consumes every subscription registered by its entrypoint
|
|
172
|
+
module. Add an optional `topics` filter to split subscriptions across
|
|
173
|
+
several subscriber functions:
|
|
174
|
+
|
|
175
|
+
```toml
|
|
176
|
+
[[tool.vercel.subscribers]]
|
|
177
|
+
entrypoint = "worker:handle_order"
|
|
178
|
+
topics = ["orders"]
|
|
179
|
+
|
|
180
|
+
[[tool.vercel.subscribers]]
|
|
181
|
+
entrypoint = "worker:handle_refund"
|
|
182
|
+
topics = ["refunds"]
|
|
185
183
|
```
|
|
186
184
|
|
|
187
185
|
### Automatic Polling Loop
|
|
@@ -268,7 +266,7 @@ polling, because it can distribute messages across regions unpredictably.
|
|
|
268
266
|
## Retry and Backoff
|
|
269
267
|
|
|
270
268
|
When a topic handler raises, the message is not acknowledged and becomes available for redelivery
|
|
271
|
-
after the `
|
|
269
|
+
after the `retry_after` interval configured on the subscriber. Retries continue until the
|
|
272
270
|
handler succeeds or the message expires.
|
|
273
271
|
|
|
274
272
|
For finer control over retry timing, raise `RetryAfter` from a subscriber:
|
|
@@ -8,6 +8,7 @@ from typing import Any
|
|
|
8
8
|
|
|
9
9
|
from hatchling.metadata.plugin.interface import MetadataHookInterface
|
|
10
10
|
from packaging.requirements import Requirement
|
|
11
|
+
from packaging.specifiers import SpecifierSet
|
|
11
12
|
|
|
12
13
|
try:
|
|
13
14
|
import tomllib
|
|
@@ -29,8 +30,10 @@ class WorkspaceDependenciesMetadataHook(MetadataHookInterface):
|
|
|
29
30
|
}
|
|
30
31
|
workspace_root = _find_workspace_root(Path(self.root))
|
|
31
32
|
|
|
33
|
+
package = pyproject.get("project", {}).get("name", str(self.root))
|
|
34
|
+
|
|
32
35
|
metadata["dependencies"] = [
|
|
33
|
-
_rewrite_dependency(requirement, workspace_names, workspace_root)
|
|
36
|
+
_rewrite_dependency(requirement, workspace_names, workspace_root, package)
|
|
34
37
|
for requirement in dependency_table.get("dependencies", [])
|
|
35
38
|
]
|
|
36
39
|
|
|
@@ -55,24 +58,56 @@ def _rewrite_dependency(
|
|
|
55
58
|
requirement: str,
|
|
56
59
|
workspace_names: set[str],
|
|
57
60
|
workspace_root: Path | None,
|
|
61
|
+
package: str,
|
|
58
62
|
) -> str:
|
|
59
63
|
parsed = Requirement(requirement)
|
|
60
64
|
normalized = parsed.name.lower().replace("_", "-")
|
|
61
65
|
if normalized not in workspace_names or workspace_root is None:
|
|
62
66
|
return requirement
|
|
63
|
-
|
|
67
|
+
version = _read_workspace_version(workspace_root, normalized)
|
|
68
|
+
return _with_lower_bound(parsed, version, requirement, package)
|
|
64
69
|
|
|
65
70
|
|
|
66
|
-
def _with_lower_bound(requirement: Requirement, version: str) -> str:
|
|
71
|
+
def _with_lower_bound(requirement: Requirement, version: str, declared: str, package: str) -> str:
|
|
67
72
|
extras = f"[{','.join(sorted(requirement.extras))}]" if requirement.extras else ""
|
|
68
73
|
specifiers = [
|
|
69
74
|
str(specifier) for specifier in requirement.specifier if specifier.operator != ">="
|
|
70
75
|
]
|
|
71
76
|
specifier_text = ",".join([f">={version}", *specifiers])
|
|
77
|
+
_reject_unsatisfiable(package, requirement.name, declared, specifier_text, version)
|
|
72
78
|
marker = f" ; {requirement.marker}" if requirement.marker else ""
|
|
73
79
|
return f"{requirement.name}{extras}{specifier_text}{marker}"
|
|
74
80
|
|
|
75
81
|
|
|
82
|
+
def _reject_unsatisfiable(
|
|
83
|
+
package: str, dependency: str, declared: str, specifier_text: str, version: str
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Refuse to publish a bound that no version of *dependency* can satisfy.
|
|
86
|
+
|
|
87
|
+
The lower bound is generated from the sibling's current version while the
|
|
88
|
+
rest of the specifier is whatever the package declared, so a hand-written
|
|
89
|
+
upper bound that the sibling has since reached produces something like
|
|
90
|
+
``>=0.3.0,<0.3.0``. Nothing rejects that later: the wheel builds, uploads,
|
|
91
|
+
and only fails when someone tries to install it. Fail the build instead --
|
|
92
|
+
CI builds every package, so the bump that crosses the bound is caught by
|
|
93
|
+
its own pull request.
|
|
94
|
+
|
|
95
|
+
The test is that the sibling version *being released* satisfies the bound,
|
|
96
|
+
which is narrower than the range being non-empty: `>=0.7.1,!=0.7.1` leaves
|
|
97
|
+
room for a later version, but says the release under way is unusable.
|
|
98
|
+
"""
|
|
99
|
+
# `prereleases=True` so a workspace version like `0.4.0b1` is judged
|
|
100
|
+
# against its own bound rather than excluded for being a prerelease.
|
|
101
|
+
if SpecifierSet(specifier_text).contains(version, prereleases=True):
|
|
102
|
+
return
|
|
103
|
+
raise RuntimeError(
|
|
104
|
+
f"{package} declares {declared!r}, but {dependency} is at {version} in "
|
|
105
|
+
f"the workspace, so publishing would pin {dependency}{specifier_text} — "
|
|
106
|
+
f"which excludes {version} itself, the version being released. "
|
|
107
|
+
f"Raise the upper bound in [tool.vercel.release.dependencies] of {package}."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
76
111
|
def _read_workspace_version(workspace_root: Path, package_name: str) -> str:
|
|
77
112
|
for pattern in ("src/*/pyproject.toml", "integrations/*/pyproject.toml"):
|
|
78
113
|
for pyproject_path in workspace_root.glob(pattern):
|
|
@@ -21,7 +21,8 @@ path = "hatch_build.py"
|
|
|
21
21
|
[tool.vercel.release.dependencies]
|
|
22
22
|
dependencies = [
|
|
23
23
|
"vercel-headers-bundle>=0.7.1",
|
|
24
|
-
"vercel-
|
|
24
|
+
"vercel-internal-core-bundle>=0.1.2,<0.2.0",
|
|
25
|
+
"vercel-oidc-bundle>=0.8.0",
|
|
25
26
|
"vercel-internal-shared-vendored-deps>=0.1.1",
|
|
26
27
|
]
|
|
27
28
|
|
|
@@ -33,6 +34,7 @@ trio = ["anyio[trio]>=4.0.0"]
|
|
|
33
34
|
[tool.uv.sources]
|
|
34
35
|
vercel-oidc = { workspace = true }
|
|
35
36
|
vercel-headers = { workspace = true }
|
|
37
|
+
vercel-internal-core = { workspace = true }
|
|
36
38
|
|
|
37
39
|
[tool.hatch.version]
|
|
38
40
|
path = "vercel/queue/version.py"
|
|
@@ -65,6 +67,10 @@ exclude = [
|
|
|
65
67
|
extend = "../../pyproject.toml"
|
|
66
68
|
cache-dir = "../../.ruff_cache"
|
|
67
69
|
preview = true
|
|
70
|
+
# `vercel` is a PEP 420 namespace package split across workspace members, so
|
|
71
|
+
# `vercel._internal.core` is this package's own shared internals rather than
|
|
72
|
+
# another project's privates. import-private-name needs telling.
|
|
73
|
+
namespace-packages = ["vercel"]
|
|
68
74
|
|
|
69
75
|
[tool.ruff.lint]
|
|
70
76
|
extend-select = [
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/subscribers.py
RENAMED
|
@@ -12,6 +12,15 @@ from importlib import import_module
|
|
|
12
12
|
from itertools import count
|
|
13
13
|
from types import MappingProxyType
|
|
14
14
|
|
|
15
|
+
from vercel._internal.core.typeutils import (
|
|
16
|
+
ResolvedAnnotation,
|
|
17
|
+
TypeAnnotationResolutionError,
|
|
18
|
+
args,
|
|
19
|
+
origin_is,
|
|
20
|
+
resolve_annotation_with_namespace_from_call_stack,
|
|
21
|
+
strip_annotated,
|
|
22
|
+
)
|
|
23
|
+
|
|
15
24
|
from .errors import (
|
|
16
25
|
DuplicateSubscriptionError,
|
|
17
26
|
PayloadValidationError,
|
|
@@ -44,14 +53,6 @@ from .types import (
|
|
|
44
53
|
Transport,
|
|
45
54
|
duration_to_seconds,
|
|
46
55
|
)
|
|
47
|
-
from .typeutils import (
|
|
48
|
-
ResolvedAnnotation,
|
|
49
|
-
TypeAnnotationResolutionError,
|
|
50
|
-
args,
|
|
51
|
-
origin_is,
|
|
52
|
-
resolve_annotation_with_namespace_from_call_stack,
|
|
53
|
-
strip_annotated,
|
|
54
|
-
)
|
|
55
56
|
|
|
56
57
|
_Subscriber: TypeAlias = Callable[..., Any | Awaitable[Any]]
|
|
57
58
|
_SubscriberRef: TypeAlias = weakref.ReferenceType[_Subscriber]
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/transports.py
RENAMED
|
@@ -7,6 +7,17 @@ import json
|
|
|
7
7
|
from collections.abc import AsyncIterable, AsyncIterator, Callable, Iterable
|
|
8
8
|
from importlib import import_module
|
|
9
9
|
|
|
10
|
+
from vercel._internal.core.typeutils import (
|
|
11
|
+
annotation_needs_resolution,
|
|
12
|
+
args,
|
|
13
|
+
is_classvar,
|
|
14
|
+
is_final,
|
|
15
|
+
is_type_var,
|
|
16
|
+
is_union_type,
|
|
17
|
+
origin_is,
|
|
18
|
+
strip_annotated,
|
|
19
|
+
)
|
|
20
|
+
|
|
10
21
|
from .constants import CONTENT_TYPE_JSON, CONTENT_TYPE_OCTET_STREAM, CONTENT_TYPE_TEXT
|
|
11
22
|
from .errors import SubscriptionError
|
|
12
23
|
from .streams import (
|
|
@@ -20,16 +31,6 @@ from .types import (
|
|
|
20
31
|
Topic,
|
|
21
32
|
Transport,
|
|
22
33
|
)
|
|
23
|
-
from .typeutils import (
|
|
24
|
-
annotation_needs_resolution,
|
|
25
|
-
args,
|
|
26
|
-
is_classvar,
|
|
27
|
-
is_final,
|
|
28
|
-
is_type_var,
|
|
29
|
-
is_union_type,
|
|
30
|
-
origin_is,
|
|
31
|
-
strip_annotated,
|
|
32
|
-
)
|
|
33
34
|
|
|
34
35
|
T = TypeVar("T")
|
|
35
36
|
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import (
|
|
4
|
-
Annotated,
|
|
5
|
-
Any,
|
|
6
|
-
ClassVar,
|
|
7
|
-
Final,
|
|
8
|
-
ForwardRef,
|
|
9
|
-
Literal,
|
|
10
|
-
TypeVar,
|
|
11
|
-
Union,
|
|
12
|
-
get_args,
|
|
13
|
-
get_origin,
|
|
14
|
-
get_type_hints,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
import inspect
|
|
18
|
-
import types
|
|
19
|
-
from dataclasses import dataclass
|
|
20
|
-
from types import FrameType
|
|
21
|
-
|
|
22
|
-
_T = TypeVar("_T")
|
|
23
|
-
_TYPE_VAR_TYPE = type(_T)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class TypeAnnotationResolutionError(TypeError):
|
|
27
|
-
"""Raised when a runtime annotation cannot be resolved."""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclass(frozen=True)
|
|
31
|
-
class ResolvedAnnotation:
|
|
32
|
-
annotation: Any
|
|
33
|
-
localns: dict[str, Any] | None = None
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def is_generic_alias(tp: Any) -> bool:
|
|
37
|
-
return get_origin(tp) is not None
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def is_annotated(tp: Any) -> bool:
|
|
41
|
-
return get_origin(tp) is Annotated
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def strip_annotated(tp: Any) -> Any:
|
|
45
|
-
while is_annotated(tp):
|
|
46
|
-
tp = get_args(tp)[0]
|
|
47
|
-
return tp
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def is_type_var(tp: Any) -> bool:
|
|
51
|
-
return isinstance(tp, _TYPE_VAR_TYPE)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def is_classvar(tp: Any) -> bool:
|
|
55
|
-
return get_origin(tp) is ClassVar
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def is_final(tp: Any) -> bool:
|
|
59
|
-
return get_origin(tp) is Final
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def is_union_type(tp: Any) -> bool:
|
|
63
|
-
return get_origin(tp) in {Union, types.UnionType}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def origin_is(tp: Any, *origins: Any) -> bool:
|
|
67
|
-
return get_origin(tp) in origins
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def args(tp: Any) -> tuple[Any, ...]:
|
|
71
|
-
return get_args(tp)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def annotation_needs_resolution(annotation: Any) -> bool:
|
|
75
|
-
if isinstance(annotation, str | ForwardRef):
|
|
76
|
-
return True
|
|
77
|
-
if is_type_var(annotation):
|
|
78
|
-
return False
|
|
79
|
-
if origin_is(annotation, Literal):
|
|
80
|
-
return False
|
|
81
|
-
if origin_is(annotation, Annotated):
|
|
82
|
-
annotation_args = args(annotation)
|
|
83
|
-
return bool(annotation_args) and annotation_needs_resolution(annotation_args[0])
|
|
84
|
-
return any(annotation_needs_resolution(item) for item in args(annotation))
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _normalize_forward_refs(annotation: Any) -> Any:
|
|
88
|
-
if isinstance(annotation, str):
|
|
89
|
-
return ForwardRef(annotation)
|
|
90
|
-
if isinstance(annotation, ForwardRef) or is_type_var(annotation):
|
|
91
|
-
return annotation
|
|
92
|
-
|
|
93
|
-
annotation_args = args(annotation)
|
|
94
|
-
if not annotation_args or origin_is(annotation, Literal):
|
|
95
|
-
return annotation
|
|
96
|
-
|
|
97
|
-
origin = get_origin(annotation)
|
|
98
|
-
if origin is Annotated:
|
|
99
|
-
first_arg = _normalize_forward_refs(annotation_args[0])
|
|
100
|
-
if first_arg is annotation_args[0]:
|
|
101
|
-
return annotation
|
|
102
|
-
return Annotated.__class_getitem__((first_arg, *annotation_args[1:]))
|
|
103
|
-
|
|
104
|
-
normalized_args = tuple(_normalize_forward_refs(item) for item in annotation_args)
|
|
105
|
-
if normalized_args == annotation_args:
|
|
106
|
-
return annotation
|
|
107
|
-
|
|
108
|
-
if isinstance(annotation, types.GenericAlias):
|
|
109
|
-
return origin[normalized_args]
|
|
110
|
-
copy_with = getattr(annotation, "copy_with", None)
|
|
111
|
-
if copy_with is not None:
|
|
112
|
-
return copy_with(normalized_args)
|
|
113
|
-
return annotation
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def _resolve_annotation_fully(
|
|
117
|
-
annotation: Any,
|
|
118
|
-
*,
|
|
119
|
-
globalns: dict[str, Any],
|
|
120
|
-
localns: dict[str, Any] | None = None,
|
|
121
|
-
) -> ResolvedAnnotation:
|
|
122
|
-
resolved = resolve_annotation(annotation, globalns=globalns, localns=localns)
|
|
123
|
-
if not annotation_needs_resolution(resolved):
|
|
124
|
-
return ResolvedAnnotation(resolved, localns)
|
|
125
|
-
|
|
126
|
-
normalized = _normalize_forward_refs(resolved)
|
|
127
|
-
return ResolvedAnnotation(
|
|
128
|
-
resolve_annotation(normalized, globalns=globalns, localns=localns),
|
|
129
|
-
localns,
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def _call_stack_localns() -> dict[str, Any]:
|
|
134
|
-
frame: FrameType | None = inspect.currentframe()
|
|
135
|
-
localns: dict[str, Any] = {}
|
|
136
|
-
try:
|
|
137
|
-
frame = frame.f_back if frame is not None else None
|
|
138
|
-
while frame is not None:
|
|
139
|
-
localns = {**frame.f_locals, **localns}
|
|
140
|
-
frame = frame.f_back
|
|
141
|
-
finally:
|
|
142
|
-
del frame
|
|
143
|
-
return localns
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def resolve_annotation(
|
|
147
|
-
annotation: Any,
|
|
148
|
-
*,
|
|
149
|
-
globalns: dict[str, Any],
|
|
150
|
-
localns: dict[str, Any] | None = None,
|
|
151
|
-
) -> Any:
|
|
152
|
-
class AnnotationShim:
|
|
153
|
-
pass
|
|
154
|
-
|
|
155
|
-
AnnotationShim.__annotations__ = {"value": annotation}
|
|
156
|
-
return get_type_hints(
|
|
157
|
-
AnnotationShim,
|
|
158
|
-
globalns=globalns,
|
|
159
|
-
localns=localns,
|
|
160
|
-
include_extras=True,
|
|
161
|
-
)["value"]
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def resolve_annotation_from_call_stack(
|
|
165
|
-
annotation: Any,
|
|
166
|
-
*,
|
|
167
|
-
globalns: dict[str, Any],
|
|
168
|
-
) -> Any:
|
|
169
|
-
return resolve_annotation_with_namespace_from_call_stack(
|
|
170
|
-
annotation,
|
|
171
|
-
globalns=globalns,
|
|
172
|
-
).annotation
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def resolve_annotation_with_namespace_from_call_stack(
|
|
176
|
-
annotation: Any,
|
|
177
|
-
*,
|
|
178
|
-
globalns: dict[str, Any],
|
|
179
|
-
) -> ResolvedAnnotation:
|
|
180
|
-
if annotation is Any or not annotation_needs_resolution(annotation):
|
|
181
|
-
return ResolvedAnnotation(annotation, _call_stack_localns())
|
|
182
|
-
|
|
183
|
-
resolution_error: BaseException | None = None
|
|
184
|
-
try:
|
|
185
|
-
return _resolve_annotation_fully(annotation, globalns=globalns)
|
|
186
|
-
except (NameError, TypeError, AttributeError) as exc:
|
|
187
|
-
resolution_error = exc
|
|
188
|
-
|
|
189
|
-
localns = _call_stack_localns()
|
|
190
|
-
|
|
191
|
-
try:
|
|
192
|
-
return _resolve_annotation_fully(
|
|
193
|
-
annotation,
|
|
194
|
-
globalns=globalns,
|
|
195
|
-
localns=localns,
|
|
196
|
-
)
|
|
197
|
-
except (NameError, TypeError, AttributeError) as exc:
|
|
198
|
-
resolution_error = exc
|
|
199
|
-
|
|
200
|
-
if not annotation_needs_resolution(annotation):
|
|
201
|
-
return ResolvedAnnotation(annotation)
|
|
202
|
-
|
|
203
|
-
raise TypeAnnotationResolutionError from resolution_error
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/api_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/asynctools.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_internal/client_sync.py
RENAMED
|
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
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/__init__.py
RENAMED
|
File without changes
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/decoders.py
RENAMED
|
File without changes
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/exceptions.py
RENAMED
|
File without changes
|
{vercel_queue_bundle-0.7.2 → vercel_queue_bundle-0.7.3}/vercel/queue/_vendor/multipart/multipart.py
RENAMED
|
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
|