A Python ASGI web framework with the same API as Flask
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Async HTTP request/response handling with comprehensive data access, form processing, file uploads, JSON support, and flexible response creation.
Represents HTTP requests with async support for body data, form processing, and file uploads.
class Request:
# Core request attributes
method: str # HTTP method (GET, POST, etc.)
path: str # URL path
query_string: bytes # Raw query string
args: ImmutableMultiDict # Parsed query string arguments
headers: Headers # Request headers
cookies: ImmutableDict # Request cookies
endpoint: str | None # Matched endpoint name
view_args: dict | None # URL rule arguments
blueprint: str | None # Current blueprint name
# Async data access methods
async def get_json(
self,
force: bool = False,
silent: bool = False,
cache: bool = True
):
"""
Get JSON data from request body.
Args:
force: Parse even if content type isn't JSON
silent: Return None instead of raising on error
cache: Cache the parsed result
Returns:
Parsed JSON data or None
"""
async def get_data(self, cache: bool = True, as_text: bool = False):
"""
Get raw request body data.
Args:
cache: Cache the data for subsequent calls
as_text: Return as text instead of bytes
Returns:
Request body as bytes or str
"""
# Async properties (use await)
@property
async def form(self) -> ImmutableMultiDict:
"""Form data from request body."""
@property
async def files(self) -> ImmutableMultiDict:
"""Uploaded files from request body."""
@property
async def json(self):
"""JSON data from request body."""
@property
async def body(self):
"""Raw request body as async iterable."""Flexible response objects with header management, cookie support, and conditional response features.
class Response:
# Core response attributes
status_code: int # HTTP status code
headers: Headers # Response headers
data: bytes # Response body data
mimetype: str | None # MIME type
content_type: str | None # Content-Type header
def set_cookie(
self,
key: str,
value: str = "",
max_age: int | None = None,
expires: datetime | None = None,
path: str | None = None,
domain: str | None = None,
secure: bool = False,
httponly: bool = False,
samesite: str | None = None
):
"""
Set response cookie.
Args:
key: Cookie name
value: Cookie value
max_age: Cookie lifetime in seconds
expires: Cookie expiration datetime
path: Cookie path
domain: Cookie domain
secure: HTTPS only flag
httponly: HTTP only flag (no JavaScript access)
samesite: SameSite attribute ('Strict', 'Lax', or 'None')
"""
def delete_cookie(
self,
key: str,
path: str = "/",
domain: str | None = None
):
"""
Delete cookie by setting empty value and past expiration.
Args:
key: Cookie name to delete
path: Cookie path
domain: Cookie domain
"""
async def make_conditional(
self,
request: Request,
accept_ranges: bool = False,
complete_length: int | None = None
):
"""
Make response conditional based on request headers.
Args:
request: The request object
accept_ranges: Whether to accept range requests
complete_length: Complete content length for range requests
"""Uploaded file wrapper with async file operations and metadata access.
class FileStorage:
# File metadata
filename: str | None # Original filename
name: str | None # Form field name
content_type: str | None # MIME content type
content_length: int | None # Content length
headers: Headers # File headers
async def save(self, dst: str, buffer_size: int = 16384):
"""
Save file to disk asynchronously.
Args:
dst: Destination file path
buffer_size: Buffer size for file operations
"""
async def read(self, size: int = -1) -> bytes:
"""
Read file data.
Args:
size: Number of bytes to read (-1 for all)
Returns:
File data as bytes
"""
async def readline(self, size: int = -1) -> bytes:
"""
Read line from file.
Args:
size: Maximum line length (-1 for unlimited)
Returns:
Line data as bytes
"""
async def readlines(self, hint: int = -1) -> list[bytes]:
"""
Read lines from file.
Args:
hint: Approximate number of bytes to read
Returns:
List of lines as bytes
"""from quart import Quart, request, jsonify
app = Quart(__name__)
@app.route('/form', methods=['POST'])
async def handle_form():
# Access form data
form_data = await request.form
username = form_data.get('username')
# Access JSON data
json_data = await request.get_json()
# Access query parameters
search = request.args.get('search', '')
# Access headers
auth_header = request.headers.get('Authorization')
return jsonify({
'username': username,
'json': json_data,
'search': search,
'auth': auth_header
})
@app.route('/upload', methods=['POST'])
async def handle_upload():
# Access uploaded files
files = await request.files
uploaded_file = files.get('document')
if uploaded_file and uploaded_file.filename:
# Save file
await uploaded_file.save(f'/uploads/{uploaded_file.filename}')
# Read file content
content = await uploaded_file.read()
return f'Uploaded {uploaded_file.filename} ({len(content)} bytes)'
return 'No file uploaded'from quart import Quart, make_response, jsonify
app = Quart(__name__)
@app.route('/custom-response')
async def custom_response():
# Create custom response
response = await make_response('Custom content')
response.status_code = 201
response.headers['X-Custom-Header'] = 'Custom Value'
# Set cookie
response.set_cookie('session_id', 'abc123', max_age=3600, httponly=True)
return response
@app.route('/json-response')
async def json_response():
# JSON response with custom headers
response = jsonify({'status': 'success', 'data': [1, 2, 3]})
response.headers['Cache-Control'] = 'no-cache'
return response
@app.route('/conditional')
async def conditional_response():
# Create conditional response
response = await make_response('Content that might be cached')
await response.make_conditional(request)
return responsefrom quart import Quart, request, g
app = Quart(__name__)
@app.before_request
async def before_request():
# Access request data in before_request
g.start_time = time.time()
g.user_agent = request.headers.get('User-Agent')
# Parse JSON for all POST requests
if request.method == 'POST':
g.json_data = await request.get_json(silent=True)
@app.route('/api/process', methods=['POST'])
async def process_data():
# Use pre-parsed data from g
data = getattr(g, 'json_data', {})
return jsonify({
'processed': data,
'user_agent': g.user_agent,
'processing_time': time.time() - g.start_time
})Install with Tessl CLI
npx tessl i tessl/pypi-quart