Require Timeouts on Python Network Calls
Catches requests calls without a timeout, disabled httpx timeouts, and boto3, Redis, and database clients left on long or unbounded defaults.
What it flags
import json
import boto3
import requests
from webhooks.signing import sign
s3 = boto3.client("s3")
def deliver_webhook(endpoint_url: str, secret: str, event: dict) -> None:
body = json.dumps(event)
s3.put_object(Bucket="webhook-archive", Key=f"{event['id']}.json", Body=body)
# No timeout: a receiver that never responds blocks this worker indefinitely
response = requests.post(
endpoint_url,
data=body,
headers={"Content-Type": "application/json", "X-Signature": sign(secret, body)},
)
response.raise_for_status()import json
import boto3
import requests
from botocore.config import Config
from webhooks.signing import sign
S3_CONFIG = Config(connect_timeout=2, read_timeout=5, retries={"max_attempts": 3})
s3 = boto3.client("s3", config=S3_CONFIG)
WEBHOOK_TIMEOUT = (3.05, 10) # (connect, read) in seconds
def deliver_webhook(endpoint_url: str, secret: str, event: dict) -> None:
body = json.dumps(event)
s3.put_object(Bucket="webhook-archive", Key=f"{event['id']}.json", Body=body)
response = requests.post(
endpoint_url,
data=body,
headers={"Content-Type": "application/json", "X-Signature": sign(secret, body)},
timeout=WEBHOOK_TIMEOUT,
)
response.raise_for_status()Why it matters
The requests library has no default timeout, so one receiver that accepts a connection and never answers pins a worker until the process restarts. When enough workers hang, queues back up and health checks fail even though nothing crashed. A requests timeout bounds connecting and each socket read, not the whole download, and SDK retries multiply every per-attempt limit, so both need deliberate values on production paths.
Agent instructions
cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.
AGENTS.md
Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.
## Require Timeouts on Python Network Calls Applies to `**/*.py`. Review Python code on production paths that calls HTTP APIs, cloud SDKs, databases, caches, or other network services for calls that can wait without a bound. Flag changes that: - Call `requests.get`, `requests.post`, `requests.request`, or a `requests.Session` method without a `timeout` argument; requests has no default timeout. - Pass `timeout=None` to a request, or build an `httpx.Client` or `httpx.AsyncClient` with `timeout=None`, which disables the limit. - Call `urllib.request.urlopen`, `smtplib.SMTP`, `ftplib.FTP`, or `socket.create_connection` without a `timeout`, which uses the global socket default of no timeout unless the application calls `socket.setdefaulttimeout()`. - Create a `boto3` client or resource for request handlers or workers without a `botocore.config.Config` that sets `connect_timeout`, `read_timeout`, and `retries`; the timeouts default to 60 seconds per attempt. - Open PostgreSQL, MySQL, or Redis connections without connect and query timeouts, such as a libpq `connect_timeout` and server-side `statement_timeout`, or redis-py `socket_timeout` and `socket_connect_timeout`. - Retry network calls in a loop with no cap on attempts or total time. - Await network work that has no timeout of its own without `asyncio.timeout()` or `asyncio.wait_for()`. Do not flag: - Calls through a shared session, client factory, or wrapper that already sets timeouts. - Scripts, notebooks, one-off management commands, and tests with mocked transports. - Streaming downloads, websockets, or long-poll calls that set an explicit, deliberately long read timeout.
Checks files matching **/*.py.
More Python agents
See all Python code review standards
Run this agent on every pull request
cubic reviews every pull request against the agents you turn on and comments on the lines that break them.