vercel-cli 48.6.6__py3-none-any.whl → 50.4.6__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.
- vercel_cli/vendor/dist/index.js +70005 -64961
- vercel_cli/vendor/dist/vc.js +4 -3
- vercel_cli/vendor/node_modules/.package-lock.json +6 -6
- vercel_cli/vendor/node_modules/@vercel/build-utils/CHANGELOG.md +132 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/framework-helpers.d.ts +5 -4
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/framework-helpers.js +28 -2
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/node-version.js +8 -3
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/read-config-file.d.ts +6 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/read-config-file.js +11 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/run-user-scripts.d.ts +25 -6
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/run-user-scripts.js +53 -11
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/generate-node-builder-functions.d.ts +8 -2
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/generate-node-builder-functions.js +4 -2
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/index.d.ts +5 -4
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/index.js +2545 -502
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/lambda.d.ts +17 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/lambda.js +11 -1
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/python.d.ts +22 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/python.js +85 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/types.d.ts +9 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/lib/python/ast_parser.py +72 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/lib/python/tests/test_ast_parser.py +72 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/package.json +4 -4
- vercel_cli/vendor/node_modules/@vercel/python/dist/index.js +910 -421
- vercel_cli/vendor/node_modules/@vercel/python/package.json +3 -3
- vercel_cli/vendor/node_modules/@vercel/python/vc_init.py +371 -161
- vercel_cli/vendor/node_modules/@vercel/python/vc_init_dev_asgi.py +3 -2
- vercel_cli/vendor/package.json +5 -4
- {vercel_cli-48.6.6.dist-info → vercel_cli-50.4.6.dist-info}/METADATA +1 -1
- {vercel_cli-48.6.6.dist-info → vercel_cli-50.4.6.dist-info}/RECORD +34 -30
- {vercel_cli-48.6.6.dist-info → vercel_cli-50.4.6.dist-info}/WHEEL +1 -1
- /vercel_cli/vendor/dist/{builder-worker.js → builder-worker.cjs} +0 -0
- /vercel_cli/vendor/dist/{get-latest-worker.js → get-latest-worker.cjs} +0 -0
- {vercel_cli-48.6.6.dist-info → vercel_cli-50.4.6.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.6",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"@types/jest": "27.4.1",
|
|
22
22
|
"@types/node": "14.18.33",
|
|
23
23
|
"@types/which": "3.0.0",
|
|
24
|
-
"@vercel/build-utils": "12.1.3",
|
|
25
24
|
"cross-env": "7.0.3",
|
|
26
25
|
"execa": "^1.0.0",
|
|
27
26
|
"fs-extra": "11.1.1",
|
|
28
27
|
"jest-junit": "16.0.0",
|
|
29
|
-
"which": "3.0.0"
|
|
28
|
+
"which": "3.0.0",
|
|
29
|
+
"@vercel/build-utils": "13.2.4"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "node ../../utils/build-builder.mjs",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sys
|
|
2
3
|
import os
|
|
3
4
|
import site
|
|
@@ -5,9 +6,21 @@ import importlib
|
|
|
5
6
|
import base64
|
|
6
7
|
import json
|
|
7
8
|
import inspect
|
|
9
|
+
import asyncio
|
|
10
|
+
import http
|
|
11
|
+
import time
|
|
12
|
+
import traceback
|
|
8
13
|
from importlib import util
|
|
9
|
-
from http.server import BaseHTTPRequestHandler
|
|
14
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
10
15
|
import socket
|
|
16
|
+
import functools
|
|
17
|
+
import logging
|
|
18
|
+
import builtins
|
|
19
|
+
from typing import Callable, Literal, TextIO
|
|
20
|
+
import contextvars
|
|
21
|
+
import contextlib
|
|
22
|
+
import atexit
|
|
23
|
+
|
|
11
24
|
|
|
12
25
|
_here = os.path.dirname(__file__)
|
|
13
26
|
_vendor_rel = '__VC_HANDLER_VENDOR_DIR'
|
|
@@ -30,13 +43,209 @@ if os.path.isdir(_vendor):
|
|
|
30
43
|
|
|
31
44
|
importlib.invalidate_caches()
|
|
32
45
|
|
|
46
|
+
|
|
47
|
+
def setup_logging(send_message: Callable[[dict], None], storage: contextvars.ContextVar[dict | None]):
|
|
48
|
+
# Override logging.Handler to send logs to the platform when a request context is available.
|
|
49
|
+
class VCLogHandler(logging.Handler):
|
|
50
|
+
def emit(self, record: logging.LogRecord):
|
|
51
|
+
try:
|
|
52
|
+
message = record.getMessage()
|
|
53
|
+
except Exception:
|
|
54
|
+
message = repr(getattr(record, "msg", ""))
|
|
55
|
+
|
|
56
|
+
with contextlib.suppress(Exception):
|
|
57
|
+
if record.exc_info:
|
|
58
|
+
# logging allows exc_info=True or a (type, value, tb) tuple
|
|
59
|
+
exc_info = record.exc_info
|
|
60
|
+
if exc_info is True:
|
|
61
|
+
exc_info = sys.exc_info()
|
|
62
|
+
if isinstance(exc_info, tuple):
|
|
63
|
+
tb = ''.join(traceback.format_exception(*exc_info))
|
|
64
|
+
if tb:
|
|
65
|
+
if message:
|
|
66
|
+
message = f"{message}\n{tb}"
|
|
67
|
+
else:
|
|
68
|
+
message = tb
|
|
69
|
+
|
|
70
|
+
if record.levelno >= logging.CRITICAL:
|
|
71
|
+
level = "fatal"
|
|
72
|
+
elif record.levelno >= logging.ERROR:
|
|
73
|
+
level = "error"
|
|
74
|
+
elif record.levelno >= logging.WARNING:
|
|
75
|
+
level = "warn"
|
|
76
|
+
elif record.levelno >= logging.INFO:
|
|
77
|
+
level = "info"
|
|
78
|
+
else:
|
|
79
|
+
level = "debug"
|
|
80
|
+
|
|
81
|
+
context = storage.get()
|
|
82
|
+
if context is not None:
|
|
83
|
+
send_message({
|
|
84
|
+
"type": "log",
|
|
85
|
+
"payload": {
|
|
86
|
+
"context": {
|
|
87
|
+
"invocationId": context['invocationId'],
|
|
88
|
+
"requestId": context['requestId'],
|
|
89
|
+
},
|
|
90
|
+
"message": base64.b64encode(message.encode()).decode(),
|
|
91
|
+
"level": level,
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
else:
|
|
95
|
+
# If IPC is not ready, enqueue the message to be sent later.
|
|
96
|
+
enqueue_or_send_message({
|
|
97
|
+
"type": "log",
|
|
98
|
+
"payload": {
|
|
99
|
+
"context": {"invocationId": "0", "requestId": 0},
|
|
100
|
+
"message": base64.b64encode(message.encode()).decode(),
|
|
101
|
+
"level": level,
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
# Override sys.stdout and sys.stderr to map logs to the correct request
|
|
106
|
+
class StreamWrapper:
|
|
107
|
+
def __init__(self, stream: TextIO, stream_name: Literal["stdout", "stderr"]):
|
|
108
|
+
self.stream = stream
|
|
109
|
+
self.stream_name = stream_name
|
|
110
|
+
|
|
111
|
+
def write(self, message: str):
|
|
112
|
+
context = storage.get()
|
|
113
|
+
if context is not None:
|
|
114
|
+
send_message({
|
|
115
|
+
"type": "log",
|
|
116
|
+
"payload": {
|
|
117
|
+
"context": {
|
|
118
|
+
"invocationId": context['invocationId'],
|
|
119
|
+
"requestId": context['requestId'],
|
|
120
|
+
},
|
|
121
|
+
"message": base64.b64encode(message.encode()).decode(),
|
|
122
|
+
"stream": self.stream_name,
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
else:
|
|
126
|
+
enqueue_or_send_message({
|
|
127
|
+
"type": "log",
|
|
128
|
+
"payload": {
|
|
129
|
+
"context": {"invocationId": "0", "requestId": 0},
|
|
130
|
+
"message": base64.b64encode(message.encode()).decode(),
|
|
131
|
+
"stream": self.stream_name,
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
def __getattr__(self, name):
|
|
136
|
+
return getattr(self.stream, name)
|
|
137
|
+
|
|
138
|
+
sys.stdout = StreamWrapper(sys.stdout, "stdout")
|
|
139
|
+
sys.stderr = StreamWrapper(sys.stderr, "stderr")
|
|
140
|
+
|
|
141
|
+
logging.basicConfig(level=logging.INFO, handlers=[VCLogHandler()], force=True)
|
|
142
|
+
|
|
143
|
+
# Ensure built-in print funnels through stdout wrapper so prints are
|
|
144
|
+
# attributed to the current request context.
|
|
145
|
+
def print_wrapper(func: Callable[..., None]) -> Callable[..., None]:
|
|
146
|
+
@functools.wraps(func)
|
|
147
|
+
def wrapper(*args, sep=' ', end='\n', file=None, flush=False):
|
|
148
|
+
if file is None:
|
|
149
|
+
file = sys.stdout
|
|
150
|
+
if file in (sys.stdout, sys.stderr):
|
|
151
|
+
file.write(sep.join(map(str, args)) + end)
|
|
152
|
+
if flush:
|
|
153
|
+
file.flush()
|
|
154
|
+
else:
|
|
155
|
+
# User specified a different file, use original print behavior
|
|
156
|
+
func(*args, sep=sep, end=end, file=file, flush=flush)
|
|
157
|
+
return wrapper
|
|
158
|
+
|
|
159
|
+
builtins.print = print_wrapper(builtins.print)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _stderr(message: str):
|
|
163
|
+
with contextlib.suppress(Exception):
|
|
164
|
+
_original_stderr.write(message + "\n")
|
|
165
|
+
_original_stderr.flush()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# If running in the platform (IPC present), logging must be setup before importing user code so that
|
|
169
|
+
# logs happening outside the request context are emitted correctly.
|
|
170
|
+
ipc_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
171
|
+
storage: contextvars.ContextVar[dict | None] = contextvars.ContextVar('storage', default=None)
|
|
172
|
+
send_message = lambda m: None
|
|
173
|
+
_original_stderr = sys.stderr
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# Buffer for pre-handshake logs (to avoid blocking IPC on startup)
|
|
177
|
+
_ipc_ready = False
|
|
178
|
+
_init_log_buf: list[dict] = []
|
|
179
|
+
_INIT_LOG_BUF_MAX_BYTES = 1_000_000
|
|
180
|
+
_init_log_buf_bytes = 0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def enqueue_or_send_message(msg: dict):
|
|
184
|
+
global _init_log_buf_bytes
|
|
185
|
+
if _ipc_ready:
|
|
186
|
+
send_message(msg)
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
enc_len = len(json.dumps(msg))
|
|
190
|
+
|
|
191
|
+
if _init_log_buf_bytes + enc_len <= _INIT_LOG_BUF_MAX_BYTES:
|
|
192
|
+
_init_log_buf.append(msg)
|
|
193
|
+
_init_log_buf_bytes += enc_len
|
|
194
|
+
else:
|
|
195
|
+
# Fallback so message is not lost if buffer is full
|
|
196
|
+
with contextlib.suppress(Exception):
|
|
197
|
+
payload = msg.get("payload", {})
|
|
198
|
+
decoded = base64.b64decode(payload.get("message", "")).decode(errors="ignore")
|
|
199
|
+
_original_stderr.write(decoded + "\n")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def flush_init_log_buf_to_stderr():
|
|
203
|
+
global _init_log_buf, _init_log_buf_bytes
|
|
204
|
+
try:
|
|
205
|
+
combined: list[str] = []
|
|
206
|
+
for m in _init_log_buf:
|
|
207
|
+
payload = m.get("payload", {})
|
|
208
|
+
msg = payload.get("message")
|
|
209
|
+
if not msg:
|
|
210
|
+
continue
|
|
211
|
+
with contextlib.suppress(Exception):
|
|
212
|
+
decoded = base64.b64decode(msg).decode(errors="ignore")
|
|
213
|
+
combined.append(decoded)
|
|
214
|
+
if combined:
|
|
215
|
+
_stderr("".join(combined))
|
|
216
|
+
except Exception:
|
|
217
|
+
pass
|
|
218
|
+
finally:
|
|
219
|
+
_init_log_buf.clear()
|
|
220
|
+
_init_log_buf_bytes = 0
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
atexit.register(flush_init_log_buf_to_stderr)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if 'VERCEL_IPC_PATH' in os.environ:
|
|
227
|
+
with contextlib.suppress(Exception):
|
|
228
|
+
ipc_sock.connect(os.getenv("VERCEL_IPC_PATH", ""))
|
|
229
|
+
|
|
230
|
+
def send_message(message: dict):
|
|
231
|
+
with contextlib.suppress(Exception):
|
|
232
|
+
ipc_sock.sendall((json.dumps(message) + '\0').encode())
|
|
233
|
+
|
|
234
|
+
setup_logging(send_message, storage)
|
|
235
|
+
|
|
236
|
+
|
|
33
237
|
# Import relative path https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
238
|
+
try:
|
|
239
|
+
user_mod_path = os.path.join(_here, "__VC_HANDLER_ENTRYPOINT") # absolute
|
|
240
|
+
__vc_spec = util.spec_from_file_location("__VC_HANDLER_MODULE_NAME", user_mod_path)
|
|
241
|
+
__vc_module = util.module_from_spec(__vc_spec)
|
|
242
|
+
sys.modules["__VC_HANDLER_MODULE_NAME"] = __vc_module
|
|
243
|
+
__vc_spec.loader.exec_module(__vc_module)
|
|
244
|
+
__vc_variables = dir(__vc_module)
|
|
245
|
+
except Exception:
|
|
246
|
+
_stderr(f'Error importing __VC_HANDLER_ENTRYPOINT:')
|
|
247
|
+
_stderr(traceback.format_exc())
|
|
248
|
+
exit(1)
|
|
40
249
|
|
|
41
250
|
_use_legacy_asyncio = sys.version_info < (3, 10)
|
|
42
251
|
|
|
@@ -51,21 +260,98 @@ def format_headers(headers, decode=False):
|
|
|
51
260
|
keyToList[key].append(value)
|
|
52
261
|
return keyToList
|
|
53
262
|
|
|
54
|
-
if 'VERCEL_IPC_PATH' in os.environ:
|
|
55
|
-
from http.server import ThreadingHTTPServer
|
|
56
|
-
import http
|
|
57
|
-
import time
|
|
58
|
-
import contextvars
|
|
59
|
-
import functools
|
|
60
|
-
import builtins
|
|
61
|
-
import logging
|
|
62
263
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
264
|
+
class ASGIMiddleware:
|
|
265
|
+
"""
|
|
266
|
+
ASGI middleware that preserves Vercel IPC semantics for request lifecycle:
|
|
267
|
+
- Handles /_vercel/ping
|
|
268
|
+
- Extracts x-vercel-internal-* headers and removes them from downstream app
|
|
269
|
+
- Sets request context into `storage` for logging/metrics
|
|
270
|
+
- Emits handler-started and end IPC messages
|
|
271
|
+
"""
|
|
272
|
+
def __init__(self, app):
|
|
273
|
+
self.app = app
|
|
274
|
+
|
|
275
|
+
async def __call__(self, scope, receive, send):
|
|
276
|
+
if scope.get('type') != 'http':
|
|
277
|
+
# Non-HTTP traffic is forwarded verbatim
|
|
278
|
+
await self.app(scope, receive, send)
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
if scope.get('path') == '/_vercel/ping':
|
|
282
|
+
await send({
|
|
283
|
+
'type': 'http.response.start',
|
|
284
|
+
'status': 200,
|
|
285
|
+
'headers': [],
|
|
286
|
+
})
|
|
287
|
+
await send({
|
|
288
|
+
'type': 'http.response.body',
|
|
289
|
+
'body': b'',
|
|
290
|
+
'more_body': False,
|
|
291
|
+
})
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
# Extract internal headers and set per-request context
|
|
295
|
+
headers_list = scope.get('headers', []) or []
|
|
296
|
+
new_headers = []
|
|
297
|
+
invocation_id = "0"
|
|
298
|
+
request_id = 0
|
|
299
|
+
|
|
300
|
+
def _b2s(b: bytes) -> str:
|
|
301
|
+
try:
|
|
302
|
+
return b.decode()
|
|
303
|
+
except Exception:
|
|
304
|
+
return ''
|
|
305
|
+
|
|
306
|
+
for k, v in headers_list:
|
|
307
|
+
key = _b2s(k).lower()
|
|
308
|
+
val = _b2s(v)
|
|
309
|
+
if key == 'x-vercel-internal-invocation-id':
|
|
310
|
+
invocation_id = val
|
|
311
|
+
continue
|
|
312
|
+
if key == 'x-vercel-internal-request-id':
|
|
313
|
+
request_id = int(val) if val.isdigit() else 0
|
|
314
|
+
continue
|
|
315
|
+
if key in ('x-vercel-internal-span-id', 'x-vercel-internal-trace-id'):
|
|
316
|
+
continue
|
|
317
|
+
new_headers.append((k, v))
|
|
318
|
+
|
|
319
|
+
new_scope = dict(scope)
|
|
320
|
+
new_scope['headers'] = new_headers
|
|
321
|
+
|
|
322
|
+
# Announce handler start and set context for logging/metrics
|
|
323
|
+
send_message({
|
|
324
|
+
"type": "handler-started",
|
|
325
|
+
"payload": {
|
|
326
|
+
"handlerStartedAt": int(time.time() * 1000),
|
|
327
|
+
"context": {
|
|
328
|
+
"invocationId": invocation_id,
|
|
329
|
+
"requestId": request_id,
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
})
|
|
66
333
|
|
|
67
|
-
|
|
68
|
-
|
|
334
|
+
token = storage.set({
|
|
335
|
+
"invocationId": invocation_id,
|
|
336
|
+
"requestId": request_id,
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
await self.app(new_scope, receive, send)
|
|
341
|
+
finally:
|
|
342
|
+
storage.reset(token)
|
|
343
|
+
send_message({
|
|
344
|
+
"type": "end",
|
|
345
|
+
"payload": {
|
|
346
|
+
"context": {
|
|
347
|
+
"invocationId": invocation_id,
|
|
348
|
+
"requestId": request_id,
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
if 'VERCEL_IPC_PATH' in os.environ:
|
|
354
|
+
start_time = time.time()
|
|
69
355
|
|
|
70
356
|
# Override urlopen from urllib3 (& requests) to send Request Metrics
|
|
71
357
|
try:
|
|
@@ -110,71 +396,6 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
110
396
|
except:
|
|
111
397
|
pass
|
|
112
398
|
|
|
113
|
-
# Override sys.stdout and sys.stderr to map logs to the correct request
|
|
114
|
-
class StreamWrapper:
|
|
115
|
-
def __init__(self, stream, stream_name):
|
|
116
|
-
self.stream = stream
|
|
117
|
-
self.stream_name = stream_name
|
|
118
|
-
|
|
119
|
-
def write(self, message):
|
|
120
|
-
context = storage.get()
|
|
121
|
-
if context is not None:
|
|
122
|
-
send_message({
|
|
123
|
-
"type": "log",
|
|
124
|
-
"payload": {
|
|
125
|
-
"context": {
|
|
126
|
-
"invocationId": context['invocationId'],
|
|
127
|
-
"requestId": context['requestId'],
|
|
128
|
-
},
|
|
129
|
-
"message": base64.b64encode(message.encode()).decode(),
|
|
130
|
-
"stream": self.stream_name,
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
else:
|
|
134
|
-
self.stream.write(message)
|
|
135
|
-
|
|
136
|
-
def __getattr__(self, name):
|
|
137
|
-
return getattr(self.stream, name)
|
|
138
|
-
|
|
139
|
-
sys.stdout = StreamWrapper(sys.stdout, "stdout")
|
|
140
|
-
sys.stderr = StreamWrapper(sys.stderr, "stderr")
|
|
141
|
-
|
|
142
|
-
# Override the global print to log to stdout
|
|
143
|
-
def print_wrapper(func):
|
|
144
|
-
@functools.wraps(func)
|
|
145
|
-
def wrapper(*args, **kwargs):
|
|
146
|
-
sys.stdout.write(' '.join(map(str, args)) + '\n')
|
|
147
|
-
return wrapper
|
|
148
|
-
builtins.print = print_wrapper(builtins.print)
|
|
149
|
-
|
|
150
|
-
# Override logging to maps logs to the correct request
|
|
151
|
-
def logging_wrapper(func, level="info"):
|
|
152
|
-
@functools.wraps(func)
|
|
153
|
-
def wrapper(*args, **kwargs):
|
|
154
|
-
context = storage.get()
|
|
155
|
-
if context is not None:
|
|
156
|
-
send_message({
|
|
157
|
-
"type": "log",
|
|
158
|
-
"payload": {
|
|
159
|
-
"context": {
|
|
160
|
-
"invocationId": context['invocationId'],
|
|
161
|
-
"requestId": context['requestId'],
|
|
162
|
-
},
|
|
163
|
-
"message": base64.b64encode(f"{args[0]}".encode()).decode(),
|
|
164
|
-
"level": level,
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
else:
|
|
168
|
-
func(*args, **kwargs)
|
|
169
|
-
return wrapper
|
|
170
|
-
|
|
171
|
-
logging.basicConfig(level=logging.INFO)
|
|
172
|
-
logging.debug = logging_wrapper(logging.debug)
|
|
173
|
-
logging.info = logging_wrapper(logging.info)
|
|
174
|
-
logging.warning = logging_wrapper(logging.warning, "warn")
|
|
175
|
-
logging.error = logging_wrapper(logging.error, "error")
|
|
176
|
-
logging.critical = logging_wrapper(logging.critical, "error")
|
|
177
|
-
|
|
178
399
|
class BaseHandler(BaseHTTPRequestHandler):
|
|
179
400
|
# Re-implementation of BaseHTTPRequestHandler's log_message method to
|
|
180
401
|
# log to stdout instead of stderr.
|
|
@@ -240,8 +461,8 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
240
461
|
if 'handler' in __vc_variables or 'Handler' in __vc_variables:
|
|
241
462
|
base = __vc_module.handler if ('handler' in __vc_variables) else __vc_module.Handler
|
|
242
463
|
if not issubclass(base, BaseHTTPRequestHandler):
|
|
243
|
-
|
|
244
|
-
|
|
464
|
+
_stderr('Handler must inherit from BaseHTTPRequestHandler')
|
|
465
|
+
_stderr('See the docs: https://vercel.com/docs/functions/serverless-functions/runtimes/python')
|
|
245
466
|
exit(1)
|
|
246
467
|
|
|
247
468
|
class Handler(BaseHandler, base):
|
|
@@ -322,80 +543,53 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
322
543
|
if hasattr(response, 'close'):
|
|
323
544
|
response.close()
|
|
324
545
|
else:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
'method': self.command,
|
|
354
|
-
'path': url.path,
|
|
355
|
-
'raw_path': url.path.encode(),
|
|
356
|
-
}
|
|
546
|
+
# ASGI: Run with Uvicorn so we get proper lifespan and protocol handling
|
|
547
|
+
try:
|
|
548
|
+
import uvicorn
|
|
549
|
+
except Exception:
|
|
550
|
+
_stderr('Uvicorn is required to run ASGI apps. Please ensure it is installed.')
|
|
551
|
+
exit(1)
|
|
552
|
+
|
|
553
|
+
# Prefer a callable app.asgi when available; some frameworks expose a boolean here
|
|
554
|
+
user_app_candidate = getattr(__vc_module.app, 'asgi', None)
|
|
555
|
+
user_app = user_app_candidate if callable(user_app_candidate) else __vc_module.app
|
|
556
|
+
asgi_app = ASGIMiddleware(user_app)
|
|
557
|
+
|
|
558
|
+
# Pre-bind a socket to obtain an ephemeral port for IPC announcement
|
|
559
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
560
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
561
|
+
sock.bind(('127.0.0.1', 0))
|
|
562
|
+
sock.listen(2048)
|
|
563
|
+
http_port = sock.getsockname()[1]
|
|
564
|
+
|
|
565
|
+
config = uvicorn.Config(
|
|
566
|
+
app=asgi_app,
|
|
567
|
+
fd=sock.fileno(),
|
|
568
|
+
lifespan='auto',
|
|
569
|
+
access_log=False,
|
|
570
|
+
log_config=None,
|
|
571
|
+
log_level='warning',
|
|
572
|
+
)
|
|
573
|
+
server = uvicorn.Server(config)
|
|
357
574
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
575
|
+
send_message({
|
|
576
|
+
"type": "server-started",
|
|
577
|
+
"payload": {
|
|
578
|
+
"initDuration": int((time.time() - start_time) * 1000),
|
|
579
|
+
"httpPort": http_port,
|
|
580
|
+
}
|
|
581
|
+
})
|
|
363
582
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
app_queue.put_nowait({'type': 'http.request', 'body': body, 'more_body': False})
|
|
370
|
-
|
|
371
|
-
# Prepare ASGI receive function
|
|
372
|
-
async def receive():
|
|
373
|
-
message = await app_queue.get()
|
|
374
|
-
return message
|
|
375
|
-
|
|
376
|
-
# Prepare ASGI send function
|
|
377
|
-
response_started = False
|
|
378
|
-
async def send(event):
|
|
379
|
-
nonlocal response_started
|
|
380
|
-
if event['type'] == 'http.response.start':
|
|
381
|
-
self.send_response(event['status'])
|
|
382
|
-
if 'headers' in event:
|
|
383
|
-
for name, value in event['headers']:
|
|
384
|
-
self.send_header(name.decode(), value.decode())
|
|
385
|
-
self.end_headers()
|
|
386
|
-
response_started = True
|
|
387
|
-
elif event['type'] == 'http.response.body':
|
|
388
|
-
self.wfile.write(event['body'])
|
|
389
|
-
if not event.get('more_body', False):
|
|
390
|
-
self.wfile.flush()
|
|
583
|
+
# Mark IPC as ready and flush any buffered init logs
|
|
584
|
+
_ipc_ready = True
|
|
585
|
+
for m in _init_log_buf:
|
|
586
|
+
send_message(m)
|
|
587
|
+
_init_log_buf.clear()
|
|
391
588
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
loop.run_until_complete(asgi_task)
|
|
397
|
-
else:
|
|
398
|
-
asyncio.run(asgi_instance)
|
|
589
|
+
# Run the server (blocking)
|
|
590
|
+
server.run()
|
|
591
|
+
# If the server ever returns, exit
|
|
592
|
+
sys.exit(0)
|
|
399
593
|
|
|
400
594
|
if 'Handler' in locals():
|
|
401
595
|
server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
|
|
@@ -406,10 +600,15 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
406
600
|
"httpPort": server.server_address[1],
|
|
407
601
|
}
|
|
408
602
|
})
|
|
603
|
+
# Mark IPC as ready and flush any buffered init logs
|
|
604
|
+
_ipc_ready = True
|
|
605
|
+
for m in _init_log_buf:
|
|
606
|
+
send_message(m)
|
|
607
|
+
_init_log_buf.clear()
|
|
409
608
|
server.serve_forever()
|
|
410
609
|
|
|
411
|
-
|
|
412
|
-
|
|
610
|
+
_stderr('Missing variable `handler` or `app` in file "__VC_HANDLER_ENTRYPOINT".')
|
|
611
|
+
_stderr('See the docs: https://vercel.com/docs/functions/serverless-functions/runtimes/python')
|
|
413
612
|
exit(1)
|
|
414
613
|
|
|
415
614
|
if 'handler' in __vc_variables or 'Handler' in __vc_variables:
|
|
@@ -626,7 +825,18 @@ elif 'app' in __vc_variables:
|
|
|
626
825
|
)
|
|
627
826
|
|
|
628
827
|
status_code = message['status']
|
|
629
|
-
|
|
828
|
+
raw_headers = message.get('headers', [])
|
|
829
|
+
|
|
830
|
+
# Headers from werkzeug transform bytes header value
|
|
831
|
+
# from b'value' to "b'value'" so we need to process
|
|
832
|
+
# ASGI headers manually
|
|
833
|
+
decoded_headers = []
|
|
834
|
+
for key, value in raw_headers:
|
|
835
|
+
decoded_key = key.decode() if isinstance(key, bytes) else key
|
|
836
|
+
decoded_value = value.decode() if isinstance(value, bytes) else value
|
|
837
|
+
decoded_headers.append((decoded_key, decoded_value))
|
|
838
|
+
|
|
839
|
+
headers = Headers(decoded_headers)
|
|
630
840
|
|
|
631
841
|
self.on_request(headers, status_code)
|
|
632
842
|
self.state = ASGICycleState.RESPONSE
|
|
@@ -39,8 +39,9 @@ if _app is None:
|
|
|
39
39
|
f"Missing 'app' in module '{USER_MODULE}'. Define `app = ...` (ASGI app)."
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
#
|
|
43
|
-
|
|
42
|
+
# Prefer a callable app.asgi when available; some frameworks expose a boolean here
|
|
43
|
+
_CAND = getattr(_app, 'asgi', None)
|
|
44
|
+
USER_ASGI_APP = _CAND if callable(_CAND) else _app
|
|
44
45
|
|
|
45
46
|
PUBLIC_DIR = 'public'
|
|
46
47
|
|
vercel_cli/vendor/package.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"vercel": "./dist/vc.js"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@vercel/build-utils": "
|
|
7
|
+
"@vercel/build-utils": "13.2.12",
|
|
8
8
|
"@vercel/detect-agent": "1.0.0",
|
|
9
|
-
"@vercel/python": "
|
|
9
|
+
"@vercel/python": "6.1.6"
|
|
10
10
|
},
|
|
11
11
|
"description": "The command-line interface for Vercel",
|
|
12
12
|
"engines": {
|
|
@@ -30,12 +30,13 @@
|
|
|
30
30
|
"dev": "echo \"'pnpm dev [command]' has been removed. Use 'pnpm vercel [command]' instead.\" && exit 1",
|
|
31
31
|
"test": "jest --reporters=default --reporters=jest-junit --env node --verbose --bail",
|
|
32
32
|
"test-dev": "pnpm test test/dev/",
|
|
33
|
-
"test-e2e": "rimraf test/fixtures/integration && pnpm test test/integration-1.test.ts test/integration-2.test.ts test/integration-3.test.ts",
|
|
33
|
+
"test-e2e-node-all-versions": "rimraf test/fixtures/integration && pnpm test test/integration-1.test.ts test/integration-2.test.ts test/integration-3.test.ts test/integration-link-env-pull.test.ts",
|
|
34
34
|
"type-check": "tsc --noEmit",
|
|
35
35
|
"vc": "pnpm vercel",
|
|
36
36
|
"vercel": "pnpm build && node ./dist/vc.js",
|
|
37
37
|
"vitest-run": "vitest --config ./vitest.config.mts",
|
|
38
38
|
"vitest-unit": "jest test/unit/ --listTests"
|
|
39
39
|
},
|
|
40
|
-
"
|
|
40
|
+
"type": "module",
|
|
41
|
+
"version": "50.4.6"
|
|
41
42
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vercel-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 50.4.6
|
|
4
4
|
Summary: Vercel CLI packaged for Python (bundled Node.js, vendored npm)
|
|
5
5
|
Project-URL: Homepage, https://github.com/nuage-studio/vercel-cli-python
|
|
6
6
|
Project-URL: Repository, https://github.com/nuage-studio/vercel-cli-python
|