AST tools
Project description
upcast
A comprehensive static analysis toolkit for Python projects. Upcast provides 12 specialized scanners to analyze code without execution, extracting insights about Django models, environment variables, HTTP requests, concurrency patterns, code complexity, and more.
- Github repository: https://github.com/mrlyc/upcast/
- Documentation: https://mrlyc.github.io/upcast/
Quick Start
# Install
pip install upcast
# Scan environment variables
upcast scan-env-vars /path/to/project
# Analyze Django models
upcast scan-django-models /path/to/django/project
# Find blocking operations
upcast scan-blocking-operations /path/to/project -o blocking.yaml
# Check cyclomatic complexity
upcast scan-complexity /path/to/project --threshold 15
Installation
pip install upcast
Common Options
All scanner commands support powerful file filtering options:
# Include specific patterns
upcast scan-env-vars . --include "app/**" --include "core/**"
# Exclude patterns
upcast scan-env-vars . --exclude "tests/**" --exclude "migrations/**"
# Disable default excludes (venv/, build/, dist/, etc.)
upcast scan-env-vars . --no-default-excludes
# Combine options
upcast scan-env-vars . --include "src/**" --exclude "**/*_test.py"
Other common options:
-o, --output FILE: Save results to file instead of stdout--format FORMAT: Choose output format (yamlorjson)-v, --verbose: Enable detailed logging
Django Scanners
scan-django-models
Analyze Django model definitions, extracting fields, relationships, and metadata.
upcast scan-django-models /path/to/django/project
Output example:
app.models.User:
module: app.models
bases:
- models.Model
fields:
username:
type: models.CharField
max_length: 150
unique: true
email:
type: models.EmailField
created_at:
type: models.DateTimeField
auto_now_add: true
relationships:
- type: ForeignKey
to: Group
field: group
Key features:
- Detects all Django model classes
- Extracts field types and parameters
- Identifies relationships (ForeignKey, ManyToMany, OneToOne)
- Captures model metadata and options
scan-django-settings
Find all references to Django settings variables and extract settings definitions from your Django project.
Basic usage (scan usages only):
upcast scan-django-settings /path/to/django/project
Scan definitions only:
upcast scan-django-settings --definitions-only /path/to/django/project
Scan both definitions and usages:
upcast scan-django-settings --combined /path/to/django/project
Output example (usages):
DEBUG:
count: 5
locations:
- file: app/views.py
line: 15
column: 8
code: settings.DEBUG
pattern: attribute_access
- file: middleware/security.py
line: 23
column: 12
code: getattr(settings, 'DEBUG', False)
pattern: getattr
Output example (definitions):
definitions:
settings.base:
DEBUG:
value: true
line: 10
type: bool
SECRET_KEY:
value: "base-secret-key"
line: 11
type: string
INSTALLED_APPS:
value: ["django.contrib.admin", "django.contrib.auth"]
line: 15
type: list
__star_imports__:
- settings.common
settings.dev:
DEBUG:
value: false
line: 5
type: bool
overrides: settings.base
DEV_MODE:
value: true
line: 6
type: bool
Output example (combined):
definitions:
settings.base:
DEBUG:
value: true
line: 10
type: bool
usages:
DEBUG:
count: 5
locations:
- file: app/views.py
line: 15
Key features:
-
Usage scanning:
- Detects
settings.VARIABLEaccess patterns - Finds
getattr(settings, ...)calls - Tracks
from django.conf import settingsimports - Aggregates usage counts per setting
- Detects
-
Definition scanning:
- Scans settings modules in
settings/orconfig/directories - Extracts setting values with type inference
- Detects inheritance via
from .base import * - Tracks which settings override base settings
- Supports dynamic imports (
importlib.import_module) - Dynamic values wrapped in backticks (e.g.,
`os.environ.get('KEY')`)
- Scans settings modules in
CLI options:
--definitions-only: Scan and output only settings definitions--usages-only: Scan and output only settings usages (default behavior)--combined: Scan and output both definitions and usages--no-usages: Skip usage scanning (faster when only definitions needed)--no-definitions: Skip definition scanning (default behavior)-o, --output FILE: Write results to YAML file-v, --verbose: Enable verbose output
scan-django-urls
Scan Django URL configurations and extract route patterns.
upcast scan-django-urls /path/to/django/project
Output example:
/api/users/:
pattern: api/users/
view: users.views.UserListView
name: user-list
methods:
- GET
- POST
file: api/urls.py
line: 15
/api/users/<int:pk>/:
pattern: api/users/<int:pk>/
view: users.views.UserDetailView
name: user-detail
methods:
- GET
- PUT
- DELETE
file: api/urls.py
line: 16
Key features:
- Extracts URL patterns from
urlpatterns - Identifies view functions and classes
- Captures route names
- Detects path converters (
<int:id>,<slug:slug>)
scan-signals
Discover Django and Celery signal definitions and handlers.
upcast scan-signals /path/to/project
Output example:
post_save:
type: django.signal
receivers:
- handler: users.signals.create_profile
sender: User
file: users/signals.py
line: 25
- handler: notifications.signals.send_welcome_email
sender: User
file: notifications/signals.py
line: 42
task_success:
type: celery.signal
receivers:
- handler: monitoring.handlers.log_task_success
file: monitoring/handlers.py
line: 15
Key features:
- Detects Django signals (pre_save, post_save, etc.)
- Finds Celery task signals
- Identifies signal receivers and senders
- Tracks decorator-based connections (
@receiver)
Code Analysis Scanners
scan-concurrency-patterns
Identify concurrency patterns including async/await, threading, and multiprocessing.
upcast scan-concurrency-patterns /path/to/project
Output example:
async_functions:
- name: fetch_data
file: api/client.py
line: 45
uses_await: true
calls:
- asyncio.gather
- aiohttp.get
thread_usage:
- pattern: threading.Thread
file: workers/processor.py
line: 78
target: process_batch
daemon: true
multiprocessing:
- pattern: multiprocessing.Pool
file: tasks/parallel.py
line: 123
workers: 4
Key features:
- Detects async/await patterns
- Identifies threading usage
- Finds multiprocessing constructs
- Tracks asyncio operations
- Flags potential concurrency issues
scan-blocking-operations
Find blocking operations that may cause performance issues in async code.
upcast scan-blocking-operations /path/to/project
Output example:
summary:
total_operations: 8
by_category:
time_based: 3
synchronization: 2
subprocess: 3
files_analyzed: 5
operations:
time_based:
- location: api/handlers.py:45:8
type: time_based.sleep
statement: time.sleep(5)
duration: 5
async_context: true
function: async_handler
synchronization:
- location: cache/manager.py:67:12
type: synchronization.lock_acquire
statement: lock.acquire(timeout=10)
timeout: 10
async_context: true
subprocess:
- location: scripts/runner.py:23:15
type: subprocess.run
statement: subprocess.run(['ls', '-la'], timeout=30)
timeout: 30
Key features:
- Detects
time.sleep()in async functions - Finds blocking lock operations
- Identifies subprocess calls without async wrappers
- Detects Django ORM
select_for_update() - Flags anti-patterns in async code
scan-unit-tests
Analyze unit test files and extract test information.
upcast scan-unit-tests /path/to/tests
Output example:
tests/test_users.py:
framework: pytest
test_count: 15
tests:
- name: test_create_user
line: 45
type: function
async: false
fixtures:
- db
- user_factory
- name: test_update_user_email
line: 67
type: function
async: false
markers:
- slow
- integration
tests/test_api.py:
framework: unittest
test_count: 8
classes:
- name: UserAPITestCase
line: 12
tests:
- test_get_user
- test_create_user
- test_delete_user
Key features:
- Detects pytest and unittest tests
- Extracts test function/method names
- Identifies async tests
- Captures fixtures and markers
- Counts tests per file
Infrastructure Scanners
scan-env-vars
Scan for environment variable usage with advanced type inference.
upcast scan-env-vars /path/to/project
Output example:
DATABASE_URL:
types:
- str
defaults:
- postgresql://localhost/db
usages:
- location: config/settings.py:15
statement: os.getenv('DATABASE_URL', 'postgresql://localhost/db')
type: str
default: postgresql://localhost/db
required: false
- location: config/database.py:8
statement: env.str('DATABASE_URL')
type: str
required: true
required: true
API_TIMEOUT:
types:
- int
defaults:
- 30
usages:
- location: api/client.py:23
statement: int(os.getenv('API_TIMEOUT', '30'))
type: int
default: 30
required: false
required: false
Key features:
- Advanced type inference from default values and conversions
- Detects
os.getenv(),os.environ[],os.environ.get() - Supports django-environ patterns (
env.int(),env.bool()) - Identifies required vs optional variables
- Aggregates all usages per variable
scan-prometheus-metrics
Extract Prometheus metrics definitions with full metadata.
upcast scan-prometheus-metrics /path/to/project
Output example:
http_requests_total:
type: counter
help: Total HTTP requests
labels:
- method
- path
- status
namespace: myapp
subsystem: api
usages:
- location: api/metrics.py:15
pattern: instantiation
statement: Counter('http_requests_total', 'Total HTTP requests', ['method', 'path', 'status'])
request_duration_seconds:
type: histogram
help: Request duration in seconds
labels:
- endpoint
buckets:
- 0.1
- 0.5
- 1.0
- 5.0
usages:
- location: middleware/metrics.py:23
pattern: instantiation
statement: Histogram('request_duration_seconds', ...)
Key features:
- Detects Counter, Gauge, Histogram, Summary
- Extracts metric names, help text, and labels
- Captures namespace and subsystem
- Identifies histogram buckets
- Supports decorator patterns
HTTP & Exception Scanners
scan-http-requests
Find HTTP and API requests throughout your codebase.
upcast scan-http-requests /path/to/project
Output example:
requests:
- location: api/client.py:45
method: GET
library: requests
statement: requests.get('https://api.example.com/users')
url: https://api.example.com/users
has_timeout: false
- location: services/http.py:67
method: POST
library: httpx
statement: httpx.post(url, json=data, timeout=30)
has_timeout: true
timeout: 30
- location: tasks/fetch.py:23
method: GET
library: urllib
statement: urllib.request.urlopen(url)
has_timeout: false
Key features:
- Detects
requests,httpx,urllib,aiohttp - Extracts HTTP methods (GET, POST, PUT, DELETE)
- Identifies URLs when possible
- Checks for timeout configuration
- Flags missing timeout warnings
scan-exception-handlers
Analyze exception handling patterns in your code.
upcast scan-exception-handlers /path/to/project
Output example:
handlers:
- location: api/views.py:45
type: try-except
exceptions:
- ValueError
- KeyError
has_bare_except: false
reraises: false
- location: services/processor.py:78
type: try-except
exceptions:
- Exception
has_bare_except: false
reraises: true
logs_error: true
- location: legacy/old_code.py:123
type: try-except
has_bare_except: true
warning: Bare except clause detected
Key features:
- Detects try-except blocks
- Identifies caught exception types
- Flags bare except clauses (anti-pattern)
- Checks for error logging
- Detects exception re-raising
Code Quality Scanners
scan-complexity
Analyze cyclomatic complexity to identify functions that may need refactoring.
upcast scan-complexity /path/to/project
Output example:
summary:
high_complexity_count: 12
files_analyzed: 28
by_severity:
warning: 7 # 11-15
high_risk: 4 # 16-20
critical: 1 # >20
modules:
app/services/user.py:
- name: process_user_registration
line: 45
end_line: 98
complexity: 14
severity: warning
description: "Validate user registration with multiple checks"
signature: "def process_user_registration(data: dict, strict: bool = True) -> Result:"
comment_lines: 8
code_lines: 54
code: |
def process_user_registration(data: dict, strict: bool = True) -> Result:
"""Validate user registration with multiple checks."""
# Check required fields
if not data.get('email'):
return Result.error('Email required')
...
Usage options:
# Scan with default threshold (11)
upcast scan-complexity /path/to/project
# Custom threshold
upcast scan-complexity . --threshold 15
# Include test files (excluded by default)
upcast scan-complexity . --include-tests
# Save to file
upcast scan-complexity . -o complexity-report.yaml
# JSON format
upcast scan-complexity . --format json
Severity levels:
- healthy (≤5): Very simple, minimal maintenance
- acceptable (6-10): Reasonable complexity
- warning (11-15): Refactoring recommended
- high_risk (16-20): Significant maintenance cost
- critical (>20): Design issues likely
Key features:
- Accurate Calculation: Counts decision points (if/elif, loops, except, boolean operators)
- Code Extraction: Full function source code included
- Comment Statistics: Uses Python tokenize for accurate comment counting
- Test Exclusion: Automatically excludes test files (configurable)
- Detailed Metadata: Function signature, docstring, line numbers
- Actionable Output: Sorted by severity with clear recommendations
Architecture
Common Utilities
Upcast uses a shared utilities package (upcast.common) for consistency:
- file_utils: File discovery, path validation, package root detection
- patterns: Glob pattern matching with configurable excludes
- ast_utils: Unified AST analysis with astroid
- export: Consistent YAML/JSON output formatting
Benefits:
- 300+ fewer lines of duplicate code
- Consistent behavior across scanners
- Better error messages
- Unified file filtering
Ke2 Features
- Static Analysis: No code execution - safe for any codebase
- 11 Specialized Scanners: Comprehensive project analysis
- Advanced Type Inference: Smart detection of types and patterns
- Powerful File Filtering: Glob-based include/exclude patterns
- Multiple Output Formats: YAML (human-readable) and JSON (machine-readable)
- Aggregated Results: Group findings by variable/model/metric name
- Cross-Platform: Works on Windows, macOS, and Linux
- Well-Tested: Comprehensive test suite with high coverage
Contributing
Contributions are welcome! Please see our Contributing Guide for details.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
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 upcast-1.0.0.tar.gz.
File metadata
- Download URL: upcast-1.0.0.tar.gz
- Upload date:
- Size: 438.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed0629488949b9cdf07d6b1a9188d59ec8d70e0eaea4ebfb32d3f11c20ecc3be
|
|
| MD5 |
9d51743750435abebb74974d734d32a9
|
|
| BLAKE2b-256 |
0f0e30428fa59bcdfbd2f8cb52ee6bce9f08b1e5a7dd1f6576eef35440f748a8
|
File details
Details for the file upcast-1.0.0-py3-none-any.whl.
File metadata
- Download URL: upcast-1.0.0-py3-none-any.whl
- Upload date:
- Size: 125.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a980ed9d671925f3aa4e3a01c81519acc1f77f73821f48547ed6c7f2d85b9a7b
|
|
| MD5 |
70d2687b881dfdb20024787baa1b7e22
|
|
| BLAKE2b-256 |
53fe2acf3bd9e06f89ca22dff4c05b9eafff455b178bd258255831d0586e7a18
|