vercel-runtime 0.5.6__tar.gz → 0.7.0__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_runtime-0.5.6 → vercel_runtime-0.7.0}/PKG-INFO +1 -1
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/pyproject.toml +1 -1
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/crons.py +50 -4
- vercel_runtime-0.7.0/src/vercel_runtime/dev.py +421 -0
- vercel_runtime-0.7.0/src/vercel_runtime/resolver.py +92 -0
- vercel_runtime-0.7.0/src/vercel_runtime/routing.py +177 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/vc_init.py +45 -182
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/workers.py +41 -1
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/LICENSE.txt +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_compat.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_termui_impl.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_textwrap.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_winconsole.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/core.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/decorators.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/exceptions.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/formatting.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/globals.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/parser.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/py.typed +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/shell_completion.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/termui.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/testing.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/types.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/utils.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/LICENSE.txt +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/ansi.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/ansitowin32.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/initialise.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/win32.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/winterm.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama.pyi +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/LICENSE.txt +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_abnf.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_connection.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_events.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_headers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_readers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_receivebuffer.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_state.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_util.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_version.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_writers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/py.typed +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/LICENSE.txt +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/_native.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/_speedups.c +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/_speedups.pyi +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/py.typed +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/LICENSE.md +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/__main__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/_compat.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/_subprocess.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/_types.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/config.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/importer.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/lifespan/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/lifespan/off.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/lifespan/on.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/logging.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/asyncio.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/auto.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/uvloop.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/main.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/asgi2.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/message_logger.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/proxy_headers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/wsgi.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/auto.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/flow_control.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/h11_impl.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/httptools_impl.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/utils.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/auto.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/websockets_impl.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/websockets_sansio_impl.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/wsproto_impl.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/py.typed +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/server.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/basereload.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/multiprocess.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/statreload.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/watchfilesreload.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/workers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/vendor.txt +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/ICON_LICENSE.md +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/LICENSE.txt +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/_internal.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/_reloader.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/accept.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/auth.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/cache_control.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/csp.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/etag.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/file_storage.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/headers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/mixins.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/range.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/structures.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/console.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/repr.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/ICON_LICENSE.md +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/console.png +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/debugger.js +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/less.png +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/more.png +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/style.css +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/tbtools.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/exceptions.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/formparser.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/http.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/local.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/dispatcher.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/http_proxy.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/lint.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/profiler.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/proxy_fix.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/shared_data.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/py.typed +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/converters.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/exceptions.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/map.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/matcher.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/rules.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/http.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/multipart.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/request.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/response.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/utils.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/security.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/serving.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/test.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/testapp.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/urls.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/user_agent.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/utils.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wrappers/__init__.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wrappers/request.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wrappers/response.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wsgi.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/asgi.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/headers.py +0 -0
- {vercel_runtime-0.5.6 → vercel_runtime-0.7.0}/src/vercel_runtime/utils.py +0 -0
|
@@ -2,9 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import hmac
|
|
5
|
+
import inspect
|
|
5
6
|
import os
|
|
6
7
|
import traceback
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
8
9
|
|
|
9
10
|
from vercel_runtime.asgi import ASGI, drain_body, get_header, send_json_response
|
|
10
11
|
from vercel_runtime.utils import (
|
|
@@ -13,7 +14,12 @@ from vercel_runtime.utils import (
|
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING:
|
|
16
|
-
from collections.abc import Awaitable, Callable
|
|
17
|
+
from collections.abc import Awaitable, Callable, Coroutine
|
|
18
|
+
from types import ModuleType
|
|
19
|
+
|
|
20
|
+
type SyncCronHandler = Callable[[], None]
|
|
21
|
+
type AsyncCronHandler = Callable[[], Coroutine[Any, Any, None]]
|
|
22
|
+
type CronHandler = SyncCronHandler | AsyncCronHandler
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
def is_cron_service() -> bool:
|
|
@@ -21,7 +27,16 @@ def is_cron_service() -> bool:
|
|
|
21
27
|
return svc_type.strip().lower() == "cron"
|
|
22
28
|
|
|
23
29
|
|
|
24
|
-
def bootstrap_cron_service_app(module:
|
|
30
|
+
def bootstrap_cron_service_app(module: ModuleType) -> ASGI:
|
|
31
|
+
# Check for a named handler function (module:function entrypoint)
|
|
32
|
+
handler_func = _resolve_handler_function(module)
|
|
33
|
+
if handler_func is not None:
|
|
34
|
+
return _make_cron_asgi_app(
|
|
35
|
+
handler_func,
|
|
36
|
+
is_async=inspect.iscoroutinefunction(handler_func),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Fall back to __main__ guard execution
|
|
25
40
|
entrypoint_abs = os.environ.get("__VC_HANDLER_ENTRYPOINT_ABS")
|
|
26
41
|
if not entrypoint_abs:
|
|
27
42
|
raise RuntimeError(
|
|
@@ -35,6 +50,34 @@ def bootstrap_cron_service_app(module: object) -> ASGI:
|
|
|
35
50
|
'The entrypoint must define an `if __name__ == "__main__"` block.'
|
|
36
51
|
)
|
|
37
52
|
|
|
53
|
+
return _make_cron_asgi_app(
|
|
54
|
+
lambda: run_entrypoint_as_main(entrypoint_abs),
|
|
55
|
+
is_async=False,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _resolve_handler_function(module: ModuleType) -> CronHandler | None:
|
|
60
|
+
func_name = os.environ.get("__VC_HANDLER_FUNC_NAME")
|
|
61
|
+
if not func_name:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
func = getattr(module, func_name, None)
|
|
65
|
+
if func is None:
|
|
66
|
+
raise RuntimeError(
|
|
67
|
+
f'cron handler function "{func_name}" not found in module'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if not callable(func):
|
|
71
|
+
raise RuntimeError(f'cron handler "{func_name}" is not callable')
|
|
72
|
+
|
|
73
|
+
return cast("CronHandler", func)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _make_cron_asgi_app(
|
|
77
|
+
run_job: CronHandler,
|
|
78
|
+
*,
|
|
79
|
+
is_async: bool,
|
|
80
|
+
) -> ASGI:
|
|
38
81
|
async def app(
|
|
39
82
|
scope: dict[str, Any],
|
|
40
83
|
receive: Callable[[], Awaitable[dict[str, Any]]],
|
|
@@ -75,7 +118,10 @@ def bootstrap_cron_service_app(module: object) -> ASGI:
|
|
|
75
118
|
await drain_body(receive)
|
|
76
119
|
|
|
77
120
|
try:
|
|
78
|
-
|
|
121
|
+
if is_async:
|
|
122
|
+
await cast("AsyncCronHandler", run_job)()
|
|
123
|
+
else:
|
|
124
|
+
await asyncio.to_thread(cast("SyncCronHandler", run_job))
|
|
79
125
|
except Exception:
|
|
80
126
|
traceback.print_exc()
|
|
81
127
|
await send_json_response(send, 500, {"error": "internal"})
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import logging.config
|
|
5
|
+
import mimetypes
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
9
|
+
|
|
10
|
+
from vercel_runtime._vendor import uvicorn as vendored_uvicorn
|
|
11
|
+
from vercel_runtime._vendor.uvicorn.config import (
|
|
12
|
+
LOGGING_CONFIG as UVICORN_LOGGING_CONFIG,
|
|
13
|
+
)
|
|
14
|
+
from vercel_runtime._vendor.werkzeug.serving import run_simple
|
|
15
|
+
from vercel_runtime.resolver import detect_app_type, import_module, resolve_app
|
|
16
|
+
from vercel_runtime.routing import (
|
|
17
|
+
apply_service_route_prefix_to_asgi_scope,
|
|
18
|
+
strip_service_route_prefix,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Awaitable, Callable
|
|
23
|
+
from wsgiref.types import WSGIApplication
|
|
24
|
+
|
|
25
|
+
from vercel_runtime.asgi import ASGI
|
|
26
|
+
|
|
27
|
+
_NO_COLOR = "NO_COLOR" in os.environ
|
|
28
|
+
|
|
29
|
+
_log = logging.getLogger("vercel.dev")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Used to split DEBUG-WARNING -> stdout and ERROR-CRITICAL -> stderr
|
|
33
|
+
class _MaxLevelFilter(logging.Filter):
|
|
34
|
+
def __init__(self, max_level: int) -> None:
|
|
35
|
+
super().__init__()
|
|
36
|
+
self.max_level = max_level
|
|
37
|
+
|
|
38
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
39
|
+
return record.levelno <= self.max_level
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _build_log_config(
|
|
43
|
+
loggers: dict[str, Any],
|
|
44
|
+
_filter_ref: object | None = None,
|
|
45
|
+
) -> dict[str, Any]:
|
|
46
|
+
if _filter_ref is None:
|
|
47
|
+
_filter_ref = "vercel_runtime.dev._MaxLevelFilter"
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
"version": 1,
|
|
51
|
+
"disable_existing_loggers": False,
|
|
52
|
+
"filters": {
|
|
53
|
+
"max_warning": {
|
|
54
|
+
"()": _filter_ref,
|
|
55
|
+
"max_level": logging.WARNING,
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"handlers": {
|
|
59
|
+
"stdout": {
|
|
60
|
+
"class": "logging.StreamHandler",
|
|
61
|
+
"stream": "ext://sys.stdout",
|
|
62
|
+
"filters": ["max_warning"],
|
|
63
|
+
},
|
|
64
|
+
"stderr": {
|
|
65
|
+
"class": "logging.StreamHandler",
|
|
66
|
+
"stream": "ext://sys.stderr",
|
|
67
|
+
"level": "ERROR",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
"loggers": loggers,
|
|
71
|
+
"root": {
|
|
72
|
+
"handlers": ["stdout", "stderr"],
|
|
73
|
+
"level": "INFO",
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _setup_server_log_routing(logger_name: str | None = None) -> None:
|
|
79
|
+
loggers: dict[str, Any] = {}
|
|
80
|
+
|
|
81
|
+
if logger_name:
|
|
82
|
+
loggers[logger_name] = {
|
|
83
|
+
"handlers": ["stdout", "stderr"],
|
|
84
|
+
"level": "INFO",
|
|
85
|
+
"propagate": False,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
logging.config.dictConfig(
|
|
89
|
+
_build_log_config(
|
|
90
|
+
loggers=loggers,
|
|
91
|
+
_filter_ref=_MaxLevelFilter,
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _build_uvicorn_log_config(
|
|
97
|
+
default_fmt: dict[str, Any] | None = None,
|
|
98
|
+
access_fmt: dict[str, Any] | None = None,
|
|
99
|
+
) -> dict[str, Any]:
|
|
100
|
+
uvicorn_fmts: dict[str, Any] = UVICORN_LOGGING_CONFIG["formatters"]
|
|
101
|
+
|
|
102
|
+
cfg = _build_log_config(
|
|
103
|
+
loggers={
|
|
104
|
+
"uvicorn": {
|
|
105
|
+
"handlers": ["stdout", "stderr"],
|
|
106
|
+
"level": "INFO",
|
|
107
|
+
"propagate": False,
|
|
108
|
+
},
|
|
109
|
+
"uvicorn.error": {"level": "INFO"},
|
|
110
|
+
"uvicorn.access": {
|
|
111
|
+
"handlers": ["access"],
|
|
112
|
+
"level": "INFO",
|
|
113
|
+
"propagate": False,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if default_fmt is None:
|
|
119
|
+
default_fmt = {
|
|
120
|
+
**uvicorn_fmts["default"],
|
|
121
|
+
"use_colors": not _NO_COLOR,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if access_fmt is None:
|
|
125
|
+
access_fmt = {
|
|
126
|
+
**uvicorn_fmts["access"],
|
|
127
|
+
"use_colors": not _NO_COLOR,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
cfg["formatters"] = {"default": default_fmt, "access": access_fmt}
|
|
131
|
+
cfg["handlers"]["stdout"]["formatter"] = "default"
|
|
132
|
+
cfg["handlers"]["stderr"]["formatter"] = "default"
|
|
133
|
+
cfg["handlers"]["access"] = {
|
|
134
|
+
"class": "logging.StreamHandler",
|
|
135
|
+
"stream": "ext://sys.stdout",
|
|
136
|
+
"formatter": "access",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return cfg
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _patch_fastapi_cli_log_config() -> None:
|
|
143
|
+
try:
|
|
144
|
+
import fastapi_cli.cli as _fcli_cli # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
|
|
145
|
+
import fastapi_cli.utils.cli as _fcli # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
|
|
146
|
+
|
|
147
|
+
orig_get_config = cast(
|
|
148
|
+
"Callable[[], dict[str, Any]]",
|
|
149
|
+
_fcli.get_uvicorn_log_config, # pyright: ignore[reportUnknownMemberType]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# we want to ensure that attr exists
|
|
153
|
+
# because we're going to patch it as well
|
|
154
|
+
_fcli_cli.get_uvicorn_log_config # noqa: B018 # pyright: ignore[reportUnknownMemberType]
|
|
155
|
+
except (ImportError, AttributeError):
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
def _get_routed_config() -> dict[str, Any]:
|
|
159
|
+
orig = orig_get_config()
|
|
160
|
+
return _build_uvicorn_log_config(
|
|
161
|
+
default_fmt={
|
|
162
|
+
"()": "fastapi_cli.utils.cli.CustomFormatter",
|
|
163
|
+
"fmt": orig["formatters"]["default"].get(
|
|
164
|
+
"fmt", "%(levelprefix)s %(message)s"
|
|
165
|
+
),
|
|
166
|
+
"use_colors": orig["formatters"]["default"].get("use_colors"),
|
|
167
|
+
},
|
|
168
|
+
access_fmt={
|
|
169
|
+
"()": "fastapi_cli.utils.cli.CustomFormatter",
|
|
170
|
+
"fmt": orig["formatters"]["access"].get(
|
|
171
|
+
"fmt",
|
|
172
|
+
"%(levelprefix)s %(client_addr)s - "
|
|
173
|
+
"'%(request_line)s' %(status_code)s",
|
|
174
|
+
),
|
|
175
|
+
},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
_fcli.get_uvicorn_log_config = _get_routed_config
|
|
179
|
+
_fcli_cli.get_uvicorn_log_config = _get_routed_config
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
PUBLIC_DIR = "public"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _build_static_asgi_app() -> ASGI | None:
|
|
186
|
+
static_files_cls: type[Any] | None = None
|
|
187
|
+
try:
|
|
188
|
+
from fastapi.staticfiles import ( # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
|
|
189
|
+
StaticFiles, # pyright: ignore[reportUnknownVariableType]
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
static_files_cls = StaticFiles # pyright: ignore[reportUnknownVariableType]
|
|
193
|
+
except Exception:
|
|
194
|
+
try:
|
|
195
|
+
from starlette.staticfiles import ( # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
|
|
196
|
+
StaticFiles, # pyright: ignore[reportUnknownVariableType]
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
static_files_cls = StaticFiles # pyright: ignore[reportUnknownVariableType]
|
|
200
|
+
except Exception:
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
if static_files_cls is not None:
|
|
204
|
+
try:
|
|
205
|
+
try:
|
|
206
|
+
return cast(
|
|
207
|
+
"ASGI",
|
|
208
|
+
static_files_cls(directory=PUBLIC_DIR, check_dir=False),
|
|
209
|
+
)
|
|
210
|
+
except TypeError:
|
|
211
|
+
return cast("ASGI", static_files_cls(directory=PUBLIC_DIR))
|
|
212
|
+
except Exception:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _is_safe_file(base_dir: str, target: str) -> bool:
|
|
219
|
+
try:
|
|
220
|
+
base = os.path.realpath(base_dir)
|
|
221
|
+
tgt = os.path.realpath(target)
|
|
222
|
+
return (
|
|
223
|
+
tgt == base or tgt.startswith(base + os.sep)
|
|
224
|
+
) and os.path.isfile(tgt)
|
|
225
|
+
except Exception:
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _static_wsgi_app(
|
|
230
|
+
environ: dict[str, Any],
|
|
231
|
+
start_response: Callable[..., Any],
|
|
232
|
+
) -> list[bytes]:
|
|
233
|
+
if environ.get("REQUEST_METHOD", "GET") not in ("GET", "HEAD"):
|
|
234
|
+
return _not_found(start_response)
|
|
235
|
+
|
|
236
|
+
req_path = environ.get("PATH_INFO", "/") or "/"
|
|
237
|
+
safe = os.path.normpath(req_path).lstrip("/")
|
|
238
|
+
full = os.path.join(PUBLIC_DIR, safe)
|
|
239
|
+
if not _is_safe_file(PUBLIC_DIR, full):
|
|
240
|
+
return _not_found(start_response)
|
|
241
|
+
|
|
242
|
+
ctype, _ = mimetypes.guess_type(full)
|
|
243
|
+
headers = [("Content-Type", ctype or "application/octet-stream")]
|
|
244
|
+
try:
|
|
245
|
+
if environ.get("REQUEST_METHOD") == "HEAD":
|
|
246
|
+
start_response("200 OK", headers)
|
|
247
|
+
return []
|
|
248
|
+
with open(full, "rb") as f:
|
|
249
|
+
data = f.read()
|
|
250
|
+
headers.append(("Content-Length", str(len(data))))
|
|
251
|
+
start_response("200 OK", headers)
|
|
252
|
+
return [data]
|
|
253
|
+
except Exception:
|
|
254
|
+
return _not_found(start_response)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _not_found(
|
|
258
|
+
start_response: Callable[..., Any],
|
|
259
|
+
) -> list[bytes]:
|
|
260
|
+
start_response(
|
|
261
|
+
"404 Not Found",
|
|
262
|
+
[("Content-Type", "text/plain; charset=utf-8")],
|
|
263
|
+
)
|
|
264
|
+
return [b"Not Found"]
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
static_asgi = _build_static_asgi_app()
|
|
268
|
+
_asgi_user_app: ASGI | None = None
|
|
269
|
+
_wsgi_user_app: WSGIApplication | None = None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def asgi_app(
|
|
273
|
+
scope: dict[str, Any],
|
|
274
|
+
receive: Callable[[], Awaitable[Any]],
|
|
275
|
+
send: Callable[[Any], Awaitable[None]],
|
|
276
|
+
) -> None:
|
|
277
|
+
effective_scope = dict(scope)
|
|
278
|
+
apply_service_route_prefix_to_asgi_scope(effective_scope)
|
|
279
|
+
|
|
280
|
+
if static_asgi is not None and effective_scope.get("type") == "http":
|
|
281
|
+
req_path = effective_scope.get("path", "/") or "/"
|
|
282
|
+
safe = os.path.normpath(req_path).lstrip("/")
|
|
283
|
+
full = os.path.join(PUBLIC_DIR, safe)
|
|
284
|
+
try:
|
|
285
|
+
base = os.path.realpath(PUBLIC_DIR)
|
|
286
|
+
target = os.path.realpath(full)
|
|
287
|
+
if (
|
|
288
|
+
target == base or target.startswith(base + os.path.sep)
|
|
289
|
+
) and os.path.isfile(target):
|
|
290
|
+
await static_asgi(effective_scope, receive, send)
|
|
291
|
+
return
|
|
292
|
+
except Exception:
|
|
293
|
+
pass
|
|
294
|
+
|
|
295
|
+
assert _asgi_user_app is not None
|
|
296
|
+
await _asgi_user_app(effective_scope, receive, send)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def wsgi_app(
|
|
300
|
+
environ: dict[str, Any],
|
|
301
|
+
start_response: Callable[..., Any],
|
|
302
|
+
) -> Any:
|
|
303
|
+
path_info, matched_prefix = strip_service_route_prefix(
|
|
304
|
+
environ.get("PATH_INFO", "/") or "/"
|
|
305
|
+
)
|
|
306
|
+
environ["PATH_INFO"] = path_info
|
|
307
|
+
if matched_prefix:
|
|
308
|
+
script_name = environ.get("SCRIPT_NAME", "") or ""
|
|
309
|
+
if script_name and script_name != "/":
|
|
310
|
+
script_name = script_name.rstrip("/")
|
|
311
|
+
else:
|
|
312
|
+
script_name = ""
|
|
313
|
+
environ["SCRIPT_NAME"] = f"{script_name}{matched_prefix}"
|
|
314
|
+
|
|
315
|
+
# Try static first; if 404 then delegate to user app
|
|
316
|
+
captured_status = ""
|
|
317
|
+
captured_headers: tuple[tuple[str, str], ...] = ()
|
|
318
|
+
body_chunks: list[bytes] = []
|
|
319
|
+
|
|
320
|
+
def capture_start_response(
|
|
321
|
+
status: str,
|
|
322
|
+
headers: list[tuple[str, str]],
|
|
323
|
+
exc_info: Any = None,
|
|
324
|
+
) -> Callable[[bytes], None]:
|
|
325
|
+
nonlocal captured_status, captured_headers
|
|
326
|
+
captured_status = status
|
|
327
|
+
captured_headers = tuple(headers)
|
|
328
|
+
|
|
329
|
+
def write(chunk: bytes) -> None:
|
|
330
|
+
body_chunks.append(chunk)
|
|
331
|
+
|
|
332
|
+
return write
|
|
333
|
+
|
|
334
|
+
result = _static_wsgi_app(environ, capture_start_response)
|
|
335
|
+
# If static handler produced 200, forward its response
|
|
336
|
+
if captured_status.startswith("200 "):
|
|
337
|
+
# Send headers and any chunks collected
|
|
338
|
+
writer = start_response(captured_status, list(captured_headers))
|
|
339
|
+
for chunk in body_chunks:
|
|
340
|
+
writer(chunk)
|
|
341
|
+
return result
|
|
342
|
+
|
|
343
|
+
# Otherwise, delegate to user's WSGI app
|
|
344
|
+
assert _wsgi_user_app is not None
|
|
345
|
+
return _wsgi_user_app(environ, start_response)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _start_wsgi(host: str, port: int) -> None:
|
|
349
|
+
_setup_server_log_routing("werkzeug")
|
|
350
|
+
run_simple(host, port, wsgi_app, use_reloader=True, threaded=True)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _start_asgi(host: str, port: int) -> None:
|
|
354
|
+
# Prefer user-installed fastapi-cli when available
|
|
355
|
+
try:
|
|
356
|
+
from fastapi_cli.cli import ( # noqa: PLC0415 # pyright: ignore[reportMissingImports]
|
|
357
|
+
dev as fastapi_dev, # pyright: ignore[reportUnknownVariableType]
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
_patch_fastapi_cli_log_config()
|
|
361
|
+
fastapi_dev(
|
|
362
|
+
entrypoint="vercel_runtime.dev:asgi_app",
|
|
363
|
+
host=host,
|
|
364
|
+
port=port,
|
|
365
|
+
reload=True,
|
|
366
|
+
)
|
|
367
|
+
sys.exit(0)
|
|
368
|
+
except (ImportError, AttributeError):
|
|
369
|
+
pass
|
|
370
|
+
|
|
371
|
+
vendored_uvicorn.run(
|
|
372
|
+
"vercel_runtime.dev:asgi_app",
|
|
373
|
+
host=host,
|
|
374
|
+
port=port,
|
|
375
|
+
use_colors=not _NO_COLOR,
|
|
376
|
+
reload=True,
|
|
377
|
+
log_config=_build_uvicorn_log_config(),
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _setup_apps() -> None:
|
|
382
|
+
global _asgi_user_app, _wsgi_user_app # noqa: PLW0603
|
|
383
|
+
|
|
384
|
+
module_name = os.environ["VERCEL_DEV_MODULE_NAME"]
|
|
385
|
+
entry_abs = os.environ["VERCEL_DEV_ENTRY_ABS"]
|
|
386
|
+
|
|
387
|
+
_setup_server_log_routing()
|
|
388
|
+
|
|
389
|
+
mod = import_module(module_name, entry_abs)
|
|
390
|
+
app_name, user_app = resolve_app(mod, module_name)
|
|
391
|
+
try:
|
|
392
|
+
result = detect_app_type(user_app, module_name, app_name)
|
|
393
|
+
except RuntimeError:
|
|
394
|
+
_log.exception("could not detect application type")
|
|
395
|
+
sys.exit(1)
|
|
396
|
+
|
|
397
|
+
if result[0] == "asgi":
|
|
398
|
+
_asgi_user_app = result[1]
|
|
399
|
+
else:
|
|
400
|
+
_wsgi_user_app = result[1]
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# Setup apps at import time so that servers reloader can pick them up.
|
|
404
|
+
if os.environ.get("VERCEL_DEV_MODULE_NAME") and os.environ.get(
|
|
405
|
+
"VERCEL_DEV_ENTRY_ABS"
|
|
406
|
+
):
|
|
407
|
+
_setup_apps()
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def main() -> None:
|
|
411
|
+
host = "127.0.0.1"
|
|
412
|
+
raw_port = os.environ.get("PORT")
|
|
413
|
+
if not raw_port:
|
|
414
|
+
_log.error("PORT environment variable is required")
|
|
415
|
+
sys.exit(1)
|
|
416
|
+
port = int(raw_port)
|
|
417
|
+
|
|
418
|
+
if _wsgi_user_app is not None:
|
|
419
|
+
_start_wsgi(host, port)
|
|
420
|
+
else:
|
|
421
|
+
_start_asgi(host, port)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import sys
|
|
5
|
+
from importlib import util as _importlib_util
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from wsgiref.types import WSGIApplication
|
|
11
|
+
|
|
12
|
+
from vercel_runtime.asgi import ASGI
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Import relative path
|
|
16
|
+
# https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
|
|
17
|
+
def import_module(name: str, path: str) -> ModuleType:
|
|
18
|
+
spec = _importlib_util.spec_from_file_location(name, path)
|
|
19
|
+
if spec is None or spec.loader is None:
|
|
20
|
+
msg = f'could not load module spec for "{name}" at {path}'
|
|
21
|
+
raise RuntimeError(msg)
|
|
22
|
+
mod = _importlib_util.module_from_spec(spec)
|
|
23
|
+
sys.modules[name] = mod
|
|
24
|
+
spec.loader.exec_module(mod)
|
|
25
|
+
return mod
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def resolve_app(mod: ModuleType, module_name: str) -> tuple[str, Any]:
|
|
29
|
+
for app_var in ("app", "application"):
|
|
30
|
+
if hasattr(mod, app_var):
|
|
31
|
+
return app_var, getattr(mod, app_var)
|
|
32
|
+
|
|
33
|
+
raise RuntimeError(
|
|
34
|
+
f"Missing 'app' or 'application' in module '{module_name}': "
|
|
35
|
+
"define `app = ...` or `application = ...` in your entrypoint"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_positional_param_count(obj: object) -> int | None:
|
|
40
|
+
try:
|
|
41
|
+
sig = inspect.signature(obj) # type: ignore[arg-type]
|
|
42
|
+
return sum(
|
|
43
|
+
1
|
|
44
|
+
for p in sig.parameters.values()
|
|
45
|
+
if p.default is inspect.Parameter.empty
|
|
46
|
+
and p.kind
|
|
47
|
+
in (
|
|
48
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
49
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
except (ValueError, TypeError):
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def detect_app_type(
|
|
57
|
+
app_obj: object,
|
|
58
|
+
module_name: str,
|
|
59
|
+
app_name: str,
|
|
60
|
+
) -> tuple[Literal["asgi"], ASGI] | tuple[Literal["wsgi"], WSGIApplication]:
|
|
61
|
+
# Try .asgi attribute if it's available and is callable
|
|
62
|
+
asgi_attr = getattr(app_obj, "asgi", None)
|
|
63
|
+
if asgi_attr is not None and callable(asgi_attr):
|
|
64
|
+
return "asgi", cast("ASGI", asgi_attr)
|
|
65
|
+
|
|
66
|
+
# For async detection, check the object itself first
|
|
67
|
+
# (works for plain functions/methods).
|
|
68
|
+
# For class instances iscoroutinefunction(obj) is False,
|
|
69
|
+
# so fall back to __call__.
|
|
70
|
+
is_async = inspect.iscoroutinefunction(app_obj)
|
|
71
|
+
if not is_async and callable(app_obj):
|
|
72
|
+
is_async = inspect.iscoroutinefunction(
|
|
73
|
+
app_obj.__call__ # pyright: ignore[reportFunctionMemberAccess]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
param_count = _get_positional_param_count(app_obj)
|
|
77
|
+
|
|
78
|
+
# that's ASGI (scope, receive, send)
|
|
79
|
+
if is_async and param_count == 3:
|
|
80
|
+
return "asgi", cast("ASGI", app_obj)
|
|
81
|
+
|
|
82
|
+
# that's WSGI (environ, start_response)
|
|
83
|
+
if param_count == 2:
|
|
84
|
+
return "wsgi", cast("WSGIApplication", app_obj)
|
|
85
|
+
|
|
86
|
+
raise RuntimeError(
|
|
87
|
+
f"Could not determine the application interface for "
|
|
88
|
+
f"'{module_name}:{app_name}'\n"
|
|
89
|
+
f"Expected either:\n"
|
|
90
|
+
f" - an ASGI app: async callable(scope, receive, send)\n"
|
|
91
|
+
f" - a WSGI app: callable(environ, start_response)"
|
|
92
|
+
)
|