A Python static analysis tool that detects heavy initialization inside AWS Lambda handlers that should be moved to module scope for faster warm starts.
Project description
pythaw
A Python static analysis tool that detects heavy initialization inside AWS Lambda handlers that should be moved to module scope for faster warm starts.
Recursively follows function calls—including across imported files—to catch indirect violations.
Requirements
Python 3.10 - 3.14 — matching the actively supported AWS Lambda Python runtimes.
Install
# pip
pip install pythaw
# uv
uv add pythaw
Quick Start
# handler.py
def lambda_handler(event, context):
# BAD: Creating a boto3 client inside the handler
# runs initialization on every invocation,
# losing the benefit of warm starts.
client = boto3.client("s3")
return client.get_object(Bucket="my-bucket", Key=event["key"])
$ pythaw check handler.py
infra/aws.py:7:15: PW001 boto3.client() should be called at module scope
→ handler.py:5:11 process()
→ service.py:5:13 S3Provider.get_client()
Found 1 violation in 1 file.
Move the client to module scope so Lambda container reuse skips the initialization:
# handler.py (fixed)
client = boto3.client("s3")
def lambda_handler(event, context):
return client.get_object(Bucket="my-bucket", Key=event["key"])
$ pythaw check handler.py
All checks passed!
Usage
pythaw check <path> # Check a file or directory
pythaw check . --format json # JSON output
pythaw check . --format github # GitHub Actions annotation format
pythaw check . --format sarif # SARIF format (Code Scanning integration)
pythaw check . --select PW001,PW002 # Enable only specific rules
pythaw check . --ignore PW003 # Disable specific rules
pythaw check . --exit-zero # Always exit with code 0
pythaw check . --statistics # Show per-rule violation counts
pythaw rules # List built-in rules
pythaw rule PW001 # Show rule details
Exit Codes
| Code | Meaning |
|---|---|
| 0 | No violations found |
| 1 | Violations found |
| 2 | Tool error (invalid config, etc.) |
Rules
| ID | Detects |
|---|---|
| PW001 | boto3.client() |
| PW002 | boto3.resource() |
| PW003 | boto3.Session() |
| PW004 | pymysql.connect() |
| PW005 | psycopg2.connect() |
| PW006 | redis.Redis() |
| PW007 | redis.StrictRedis() |
| PW008 | httpx.Client() |
| PW009 | requests.Session() |
Call Graph Traversal
pythaw recursively follows local function calls and imported modules from the handler, detecting indirect violations across files.
Supported patterns
| Pattern | Example |
|---|---|
| Same-file function call | helper() |
| Module-qualified function call | infra.get_client() |
| Class method call | AwsProvider.get_client() |
Class instantiation (__init__) |
S3Client() |
| Cross-file import tracking | from infra import get_client |
Note: Instance method calls via variables (e.g.
obj = Cls(); obj.method()) are not tracked — this would require data-flow analysis beyond the current scope.
infra/aws.py:4:15: PW001 boto3.client() should be called at module scope
→ handler.py:2:10 get_client()
Found 1 violation in 1 file.
Suppression
Inline suppression
Append # nopw: <code> to a line to suppress that violation. Multiple codes can be comma-separated.
client = boto3.client("s3") # nopw: PW001
This also works on wrapper function calls — violations found recursively through the call are suppressed:
client = make_client() # nopw: PW001
File-level suppression
Add # pythaw: nocheck in the leading comment block to skip the entire file.
# pythaw: nocheck
import boto3
def handler(event, context):
boto3.client("s3") # not checked
Configuration
Configure via the [tool.pythaw] section in pyproject.toml.
[tool.pythaw]
# Function name patterns recognized as handlers (fnmatch syntax)
handler_patterns = ["handler", "lambda_handler", "*_handler"]
# Patterns to exclude from scanning
exclude = [".venv", "tests"]
# Disable specific rules per file pattern
[tool.pythaw.per-file-ignores]
"tests/*" = ["PW001", "PW002"]
"scripts/*" = ["PW001"]
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pythaw-0.3.0.tar.gz.
File metadata
- Download URL: pythaw-0.3.0.tar.gz
- Upload date:
- Size: 16.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed9b2d2bcf42b61750624f9fd666e38128206d78c718b4fc5d02d26643e2887b
|
|
| MD5 |
597d87ad9b80fa9d73ea7bf7a3954ac8
|
|
| BLAKE2b-256 |
c3a35db2c267ba52753761090d0c34f578a8a0fa087630436f50fda86c37c772
|
File details
Details for the file pythaw-0.3.0-py3-none-any.whl.
File metadata
- Download URL: pythaw-0.3.0-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb1cfc780e7bf4eb8e4607042a3d8e33c13bf54b23d77987c67a1d3e14f04840
|
|
| MD5 |
6d14512717658d4f31259d95b4fa9ec5
|
|
| BLAKE2b-256 |
5999288ca6d4133646db50e2214f273c65fbb1d0775fd0ea93ba40b9432f926a
|