Update app and tooling
This commit is contained in:
parent
3046531bdd
commit
e620ec7349
4950 changed files with 2975120 additions and 10 deletions
72
node_modules/@vercel/build-utils/lib/python/ast_parser.py
generated
vendored
Normal file
72
node_modules/@vercel/build-utils/lib/python/ast_parser.py
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import sys
|
||||
import ast
|
||||
|
||||
|
||||
def contains_app_or_handler(file_path: str) -> bool:
|
||||
"""
|
||||
Check if a Python file contains or exports:
|
||||
- A top-level 'app' callable (e.g., Flask, FastAPI, Sanic apps)
|
||||
- A top-level 'handler' class (e.g., BaseHTTPRequestHandler subclass)
|
||||
"""
|
||||
with open(file_path, "r") as file:
|
||||
code = file.read()
|
||||
|
||||
try:
|
||||
tree = ast.parse(code)
|
||||
except SyntaxError:
|
||||
return False
|
||||
|
||||
for node in ast.iter_child_nodes(tree):
|
||||
# Check for top-level assignment to 'app'
|
||||
# e.g., app = Sanic() or app = Flask(__name__) or app = create_app()
|
||||
if isinstance(node, ast.Assign):
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Name) and target.id == "app":
|
||||
return True
|
||||
|
||||
# Check for annotated assignment to 'app'
|
||||
# e.g., app: Sanic = Sanic()
|
||||
if isinstance(node, ast.AnnAssign):
|
||||
if isinstance(node.target, ast.Name) and node.target.id == "app":
|
||||
return True
|
||||
|
||||
# Check for function named 'app'
|
||||
# e.g., def app(environ, start_response): ...
|
||||
if isinstance(node, ast.FunctionDef) and node.name == "app":
|
||||
return True
|
||||
|
||||
# Check for async function named 'app'
|
||||
# e.g., async def app(scope, receive, send): ...
|
||||
if isinstance(node, ast.AsyncFunctionDef) and node.name == "app":
|
||||
return True
|
||||
|
||||
# Check for import of 'app'
|
||||
# e.g., from server import app
|
||||
# e.g., from server import application as app
|
||||
if isinstance(node, ast.ImportFrom):
|
||||
for alias in node.names:
|
||||
# alias.asname is the 'as' name, alias.name is the original name
|
||||
# If aliased, check asname; otherwise check the original name
|
||||
imported_as = alias.asname if alias.asname else alias.name
|
||||
if imported_as == "app":
|
||||
return True
|
||||
|
||||
# Check for top-level class named 'handler'
|
||||
# e.g., class handler(BaseHTTPRequestHandler):
|
||||
if isinstance(node, ast.ClassDef) and node.name.lower() == "handler":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python ast_parser.py <file_path>")
|
||||
sys.exit(1)
|
||||
|
||||
file_path = sys.argv[1]
|
||||
result = contains_app_or_handler(file_path)
|
||||
|
||||
# Exit with 0 if found, 1 if not found
|
||||
sys.exit(0 if result else 1)
|
||||
|
||||
72
node_modules/@vercel/build-utils/lib/python/tests/test_ast_parser.py
generated
vendored
Normal file
72
node_modules/@vercel/build-utils/lib/python/tests/test_ast_parser.py
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import unittest
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from ast_parser import contains_app_or_handler
|
||||
|
||||
|
||||
class TestContainsAppOrHandler(unittest.TestCase):
|
||||
def _check(self, code: str) -> bool:
|
||||
"""Helper to test code snippets without needing fixture files."""
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
||||
f.write(code)
|
||||
f.flush()
|
||||
try:
|
||||
return contains_app_or_handler(f.name)
|
||||
finally:
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_flask_app(self):
|
||||
self.assertTrue(self._check("from flask import Flask\napp = Flask(__name__)"))
|
||||
|
||||
def test_fastapi_app(self):
|
||||
self.assertTrue(self._check("from fastapi import FastAPI\napp = FastAPI()"))
|
||||
|
||||
def test_sanic_app(self):
|
||||
self.assertTrue(self._check("from sanic import Sanic\napp = Sanic('app')"))
|
||||
|
||||
def test_annotated_app(self):
|
||||
self.assertTrue(self._check("from fastapi import FastAPI\napp: FastAPI = FastAPI()"))
|
||||
|
||||
def test_wsgi_function(self):
|
||||
self.assertTrue(self._check("def app(environ, start_response):\n pass"))
|
||||
|
||||
def test_asgi_function(self):
|
||||
self.assertTrue(self._check("async def app(scope, receive, send):\n pass"))
|
||||
|
||||
def test_imported_app(self):
|
||||
self.assertTrue(self._check("from server import app"))
|
||||
|
||||
def test_imported_app_aliased(self):
|
||||
self.assertTrue(self._check("from server import application as app"))
|
||||
|
||||
def test_handler_class(self):
|
||||
self.assertTrue(self._check("class Handler:\n pass"))
|
||||
|
||||
def test_handler_class_lowercase(self):
|
||||
self.assertTrue(self._check("class handler:\n pass"))
|
||||
|
||||
def test_no_app_or_handler(self):
|
||||
self.assertFalse(self._check("def hello():\n return 'world'"))
|
||||
|
||||
def test_app_in_function_not_toplevel(self):
|
||||
# app defined inside a function should NOT match
|
||||
self.assertFalse(self._check("def create():\n app = Flask(__name__)\n return app"))
|
||||
|
||||
def test_syntax_error(self):
|
||||
self.assertFalse(self._check("def broken("))
|
||||
|
||||
def test_empty_file(self):
|
||||
self.assertFalse(self._check(""))
|
||||
|
||||
def test_only_comments(self):
|
||||
self.assertFalse(self._check("# just a comment\n# another comment"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue