Static analysis tool for tracing Python API calls to their origin library
Project description
PCResolve — Python API Call Chain Tracing
Static analysis tool that traces every API call in a Python project back to its origin library (e.g. requests, numpy, flask). Each call is classified by definition origin (not data flow): a locally defined function is local even if it internally calls third-party APIs. Other classifications include python for builtins and the top-level library name for third-party / stdlib calls. When container iteration produces ambiguous candidates, the result is a merged label like [requests,numpy].
Installation
pip install pcresolve
For development:
pip install -e .
No third-party dependencies — pcresolve uses only the Python standard library (ast, os, sys, builtins, copy, typing).
Requires Python 3.9+.
Quick Start
CLI
# Human-readable output
pcresolve /path/to/project
# JSON output
pcresolve --json /path/to/project
# Or as a module
python -m pcresolve /path/to/project
Library API
from pcresolve import analyze_project, analyze_source
# Analyze an entire project
result = analyze_project("/path/to/project")
for call in result.all_api_calls:
print(f"{call.expression} -> {call.top_library}")
# Analyze a single source string
code = '''
import requests
resp = requests.get("https://example.com")
'''
result = analyze_source(code, file_path="example.py")
for sym, top in result.symbols.items():
print(f"{sym} -> {top}")
Public API
| Name | Description |
|---|---|
analyze_project(root) |
Full project analysis, returns ProjectAnalysis |
analyze_source(code) |
Single-file analysis, returns FileAnalysis |
scan_directory(root) |
Discover .py/.pyi files, returns list[str] |
ProjectAnalyzer(root) |
Orchestrator class (step-by-step control) |
SingleFileAnalyzer() |
AST visitor class for one file |
ModuleMapper(root) |
File-path to module-name bidirectional mapping |
SymbolTable() |
Per-symbol chain tracking |
FileScanner() |
File system scanner |
Output Types
@dataclass
class ApiCall:
expression: str # "requests.get('url')"
top_library: str # "requests", "python", "local"
base_symbol: str # Root symbol name
chain: list # Resolution chain
@dataclass
class FileAnalysis:
file_path: str
module_name: str
symbols: dict # symbol -> top-level source
chains: dict # symbol -> resolution chain
api_calls: list
@dataclass
class ProjectAnalysis:
project_root: str
files: list
all_api_calls: list
Supported Patterns
- Direct import + direct call
from/asimport + alias call- Variable binding / container storage (dict, list, tuple, set)
partial/ lambda wrappers- Class encapsulation & inheritance
- Cross-file shared third-party instances
- Decorator pattern
- Context managers / protocols (
with,async with) - Chained calls / fluent API
getattr/importlib.import_moduledynamic callsforloop container iteration (with merged ambiguous candidates)- Tuple unpacking (e.g.,
a, b = connect()) - List / set / dict comprehension
- BinOp receiver method calls (e.g.,
(a - b).method()) - Broken attribute chains across
Callnodes (e.g.,.last().dt.days()) - Self-referencing variable reassignment (e.g.,
df = df.dropna()) async def/async forsupport- Wildcard import from local modules
Known Limitations
partialalias:a = partial; a(requests.get, ...)resolves tofunctoolsinstead ofrequests. The static analysis only recognizes directpartial(...)calls, not aliases topartial.- Multiple inheritance method attribution: When a class inherits from multiple bases, method resolution stops at the first base that traces to an external library. A locally defined method may be misattributed if an external base class appears earlier in the MRO.
- Container iteration in single-file mode:
for f in {requests.get, np.sum}: f(...)produces an unresolved structured tuple inSingleFileAnalyzer. Full resolution to a merged candidate like[requests,numpy]happens only in the cross-file stage. - Dynamic
import_modulewith variables:importlib.import_module(name)only resolves whennameis a string literal. Variable arguments produce an unresolved result. - Multiple return values: When a function has multiple
returnstatements, only the first encountered return value is recorded. Later branches (e.g.,elsepaths) are not tracked.
Running Tests
python -m pytest tests/ -v
License
PCResolve is licensed under the MIT License. See LICENSE for details.
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 pcresolve-1.0.3.tar.gz.
File metadata
- Download URL: pcresolve-1.0.3.tar.gz
- Upload date:
- Size: 45.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffbd287f54b9d599cea8ac5fb2975de1014f8c924c3e54eabc9e8a0bbfde7f2f
|
|
| MD5 |
8d28e4459a73fbc4daeb71b7a10db00e
|
|
| BLAKE2b-256 |
8414cbcf3e7d1c31d64978102ed3e57600dab07ed347eae1896e16c75b333320
|
File details
Details for the file pcresolve-1.0.3-py3-none-any.whl.
File metadata
- Download URL: pcresolve-1.0.3-py3-none-any.whl
- Upload date:
- Size: 26.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bb649cd455e4cccc8573661510c58bd853b50396fad092ee2c9c078e291eadd
|
|
| MD5 |
81e0ac056e2d13a7824eda461350d64b
|
|
| BLAKE2b-256 |
79a0d5278da145458c0959720d062f707fd3d2520b1aceee9f0a6b5eb7619643
|