mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-03 11:11:30 +08:00
asgi: Fix pylint and pycodestyle warnings
- Remove unused imports (ssl, os, base64, hashlib, traceback) - Remove unused variables (body_parts, has_content_length, etc.) - Fix no-else-break patterns in protocol.py and websocket.py - Replace __anext__() with anext() builtin - Remove unnecessary pass statements - Add proper access logging to ASGI protocol handler - Add ASGIResponseInfo class and _build_environ method for logging - Disable too-many-return-statements for _read_frame method - Fix raising-bad-type error (use 'is not None' check) - Fix whitespace before colon in message.py
This commit is contained in:
parent
ae1eea8108
commit
11c6a97c47
@ -477,7 +477,7 @@ class AsyncRequest:
|
||||
self._body_reader = self._chunked_body_reader()
|
||||
|
||||
try:
|
||||
return await self._body_reader.__anext__()
|
||||
return await anext(self._body_reader)
|
||||
except StopAsyncIteration:
|
||||
self._body_remaining = 0
|
||||
return b""
|
||||
@ -489,7 +489,7 @@ class AsyncRequest:
|
||||
size_line = await self._read_chunk_size_line()
|
||||
# Parse chunk size (handle extensions)
|
||||
chunk_size, *_ = size_line.split(b";", 1)
|
||||
if _ :
|
||||
if _:
|
||||
chunk_size = chunk_size.rstrip(b" \t")
|
||||
|
||||
if any(n not in b"0123456789abcdefABCDEF" for n in chunk_size):
|
||||
|
||||
@ -10,9 +10,6 @@ to ASGI applications.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import hashlib
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from gunicorn.asgi.unreader import AsyncUnreader
|
||||
@ -20,6 +17,22 @@ from gunicorn.asgi.message import AsyncRequest
|
||||
from gunicorn.http.errors import NoMoreData
|
||||
|
||||
|
||||
class ASGIResponseInfo:
|
||||
"""Simple container for ASGI response info for access logging."""
|
||||
|
||||
def __init__(self, status, headers, sent):
|
||||
self.status = status
|
||||
self.sent = sent
|
||||
# Convert headers to list of string tuples for logging
|
||||
self.headers = []
|
||||
for name, value in headers:
|
||||
if isinstance(name, bytes):
|
||||
name = name.decode("latin-1")
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode("latin-1")
|
||||
self.headers.append((name, value))
|
||||
|
||||
|
||||
class ASGIProtocol(asyncio.Protocol):
|
||||
"""HTTP/1.1 protocol handler for ASGI applications.
|
||||
|
||||
@ -97,30 +110,30 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
if self._is_websocket_upgrade(request):
|
||||
await self._handle_websocket(request, sockname, peername)
|
||||
break # WebSocket takes over the connection
|
||||
else:
|
||||
# Handle HTTP request
|
||||
keepalive = await self._handle_http_request(
|
||||
request, sockname, peername
|
||||
)
|
||||
|
||||
# Increment worker request count
|
||||
self.worker.nr += 1
|
||||
# Handle HTTP request
|
||||
keepalive = await self._handle_http_request(
|
||||
request, sockname, peername
|
||||
)
|
||||
|
||||
# Check max_requests
|
||||
if self.worker.nr >= self.worker.max_requests:
|
||||
self.log.info("Autorestarting worker after current request.")
|
||||
self.worker.alive = False
|
||||
keepalive = False
|
||||
# Increment worker request count
|
||||
self.worker.nr += 1
|
||||
|
||||
if not keepalive or not self.worker.alive:
|
||||
break
|
||||
# Check max_requests
|
||||
if self.worker.nr >= self.worker.max_requests:
|
||||
self.log.info("Autorestarting worker after current request.")
|
||||
self.worker.alive = False
|
||||
keepalive = False
|
||||
|
||||
# Check connection limits for keepalive
|
||||
if not self.cfg.keepalive:
|
||||
break
|
||||
if not keepalive or not self.worker.alive:
|
||||
break
|
||||
|
||||
# Drain any unread body before next request
|
||||
await request.drain_body()
|
||||
# Check connection limits for keepalive
|
||||
if not self.cfg.keepalive:
|
||||
break
|
||||
|
||||
# Drain any unread body before next request
|
||||
await request.drain_body()
|
||||
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
@ -155,9 +168,13 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
scope = self._build_http_scope(request, sockname, peername)
|
||||
response_started = False
|
||||
response_complete = False
|
||||
body_parts = []
|
||||
exc_to_raise = None
|
||||
|
||||
# Response tracking for access logging
|
||||
response_status = 500
|
||||
response_headers = []
|
||||
response_sent = 0
|
||||
|
||||
# Receive queue for body
|
||||
receive_queue = asyncio.Queue()
|
||||
|
||||
@ -177,6 +194,7 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
|
||||
async def send(message):
|
||||
nonlocal response_started, response_complete, exc_to_raise
|
||||
nonlocal response_status, response_headers, response_sent
|
||||
|
||||
msg_type = message["type"]
|
||||
|
||||
@ -185,9 +203,9 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
exc_to_raise = RuntimeError("Response already started")
|
||||
return
|
||||
response_started = True
|
||||
status = message["status"]
|
||||
headers = message.get("headers", [])
|
||||
await self._send_response_start(status, headers, request)
|
||||
response_status = message["status"]
|
||||
response_headers = message.get("headers", [])
|
||||
await self._send_response_start(response_status, response_headers, request)
|
||||
|
||||
elif msg_type == "http.response.body":
|
||||
if not response_started:
|
||||
@ -202,32 +220,42 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
|
||||
if body:
|
||||
await self._send_body(body)
|
||||
response_sent += len(body)
|
||||
|
||||
if not more_body:
|
||||
response_complete = True
|
||||
|
||||
# Build environ for logging
|
||||
environ = self._build_environ(request, sockname, peername)
|
||||
resp = None
|
||||
|
||||
try:
|
||||
request_start = datetime.now()
|
||||
self.cfg.pre_request(self.worker, request)
|
||||
|
||||
await self.app(scope, receive, send)
|
||||
|
||||
if exc_to_raise:
|
||||
if exc_to_raise is not None:
|
||||
raise exc_to_raise
|
||||
|
||||
# Ensure response was sent
|
||||
if not response_started:
|
||||
await self._send_error_response(500, "Internal Server Error")
|
||||
response_status = 500
|
||||
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
self.log.exception("Error in ASGI application")
|
||||
if not response_started:
|
||||
await self._send_error_response(500, "Internal Server Error")
|
||||
response_status = 500
|
||||
return False
|
||||
finally:
|
||||
try:
|
||||
request_time = datetime.now() - request_start
|
||||
self.cfg.post_request(self.worker, request, {}, None)
|
||||
# Create response info for logging
|
||||
resp = ASGIResponseInfo(response_status, response_headers, response_sent)
|
||||
self.log.access(resp, request, environ, request_time)
|
||||
self.cfg.post_request(self.worker, request, environ, resp)
|
||||
except Exception:
|
||||
self.log.exception("Exception in post_request hook")
|
||||
|
||||
@ -291,6 +319,24 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
|
||||
return scope
|
||||
|
||||
def _build_environ(self, request, sockname, peername):
|
||||
"""Build minimal WSGI-like environ dict for access logging."""
|
||||
environ = {
|
||||
"REQUEST_METHOD": request.method,
|
||||
"RAW_URI": request.uri,
|
||||
"PATH_INFO": request.path,
|
||||
"QUERY_STRING": request.query or "",
|
||||
"SERVER_PROTOCOL": f"HTTP/{request.version[0]}.{request.version[1]}",
|
||||
"REMOTE_ADDR": peername[0] if peername else "-",
|
||||
}
|
||||
|
||||
# Add HTTP headers as environ vars
|
||||
for name, value in request.headers:
|
||||
key = "HTTP_" + name.replace("-", "_")
|
||||
environ[key] = value
|
||||
|
||||
return environ
|
||||
|
||||
def _build_websocket_scope(self, request, sockname, peername):
|
||||
"""Build ASGI WebSocket scope from parsed request."""
|
||||
# Build headers list as bytes tuples
|
||||
@ -334,9 +380,6 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
|
||||
# Build headers
|
||||
header_lines = []
|
||||
has_content_length = False
|
||||
has_transfer_encoding = False
|
||||
has_connection = False
|
||||
|
||||
for name, value in headers:
|
||||
if isinstance(name, bytes):
|
||||
@ -344,13 +387,6 @@ class ASGIProtocol(asyncio.Protocol):
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode("latin-1")
|
||||
header_lines.append(f"{name}: {value}\r\n")
|
||||
name_lower = name.lower()
|
||||
if name_lower == "content-length":
|
||||
has_content_length = True
|
||||
elif name_lower == "transfer-encoding":
|
||||
has_transfer_encoding = True
|
||||
elif name_lower == "connection":
|
||||
has_connection = True
|
||||
|
||||
# Add server header if not present
|
||||
header_lines.append("Server: gunicorn/asgi\r\n")
|
||||
|
||||
@ -12,7 +12,6 @@ import asyncio
|
||||
import base64
|
||||
import hashlib
|
||||
import struct
|
||||
import os
|
||||
|
||||
|
||||
# WebSocket frame opcodes
|
||||
@ -81,7 +80,7 @@ class WebSocketProtocol:
|
||||
|
||||
try:
|
||||
await self.app(self.scope, self._receive, self._send)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
self.log.exception("Error in WebSocket ASGI application")
|
||||
finally:
|
||||
read_task.cancel()
|
||||
@ -180,7 +179,8 @@ class WebSocketProtocol:
|
||||
if opcode == OPCODE_CLOSE:
|
||||
await self._handle_close(payload)
|
||||
break
|
||||
elif opcode == OPCODE_PING:
|
||||
|
||||
if opcode == OPCODE_PING:
|
||||
await self._send_frame(OPCODE_PONG, payload)
|
||||
elif opcode == OPCODE_PONG:
|
||||
# Ignore pongs
|
||||
@ -212,7 +212,7 @@ class WebSocketProtocol:
|
||||
"code": self.close_code or CLOSE_ABNORMAL,
|
||||
})
|
||||
|
||||
async def _read_frame(self):
|
||||
async def _read_frame(self): # pylint: disable=too-many-return-statements
|
||||
"""Read a single WebSocket frame.
|
||||
|
||||
Returns:
|
||||
@ -326,10 +326,9 @@ class WebSocketProtocol:
|
||||
|
||||
self.closed = True
|
||||
|
||||
async def _handle_continuation(self, payload):
|
||||
async def _handle_continuation(self, payload): # pylint: disable=unused-argument
|
||||
"""Handle continuation frame (already processed in _read_frame)."""
|
||||
# This is called for partial fragments, nothing to do
|
||||
pass
|
||||
# This is called for partial fragments, nothing to do here
|
||||
|
||||
async def _send_frame(self, opcode, payload):
|
||||
"""Send a WebSocket frame.
|
||||
|
||||
@ -12,7 +12,6 @@ HTTP parsing infrastructure.
|
||||
import asyncio
|
||||
import os
|
||||
import signal
|
||||
import ssl
|
||||
import sys
|
||||
|
||||
from gunicorn.workers import base
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user