PyAnalysis is a command-line tool and library designed to analyze the structure of Python source code files. It leverages Python's built-in Abstract Syntax Trees (AST) module to parse the code and extracts detailed information about its components. The results are presented in a clean, structured JSON format.
This tool is useful for:
- Understanding the layout and components of unfamiliar Python modules.
- Generating code documentation inputs.
- Programmatic analysis of Python codebases.
- Educational purposes for learning about ASTs and code structure.
- AST-Based Analysis: Parses Python code using the
astmodule for accurate structural representation. - Comprehensive Extraction: Identifies and extracts information about:
- Module-level docstrings.
- Imports (
import x,import x as y). - From-imports (
from x import y,from x import y as z). - Global constants (following
SCREAMING_SNAKE_CASEconvention). - Global variables.
- Functions (top-level and nested):
- Parameters (name, type hint, default value, kind - positional-only, keyword-only, varargs, etc.).
- Return type hints.
- Decorators.
- Docstrings.
- Local variables declared within the function.
- Classes (top-level and nested):
- Base classes (inheritance).
- Decorators.
- Methods (instance methods, class methods, static methods, properties).
- Class variables.
- Instance variables (identified via
self.xassignments in instance methods). - Class variables (identified via
cls.xassignments in class/static methods or direct assignments in the class body). - Docstrings.
- Detection of
if __name__ == "__main__":blocks.
- JSON Output: Provides results in a structured JSON format, suitable for machine reading or easy human inspection.
- CLI Interface: Easy-to-use command-line tool.
- Output Options:
- Print JSON to standard output.
- Write JSON to a specified file.
- Control JSON formatting (pretty-printed or compact).
- Optionally copy the JSON output to the clipboard (requires
pyperclip).
- Error Handling: Gracefully handles file not found errors, syntax errors during parsing, and internal analysis errors, reporting them within the JSON output.
- Library Usage: Core components can be imported and used programmatically.
-
Clone the repository:
git clone https://github.com/AlexandrosLiaskos/PyAnalysis.git cd PyAnalysis -
Install the package: Using pip, you can install the package locally. This makes the
python -m pyanalyzercommand available in your environment.pip install .- Alternatively, for development:
pip install -e .
- Alternatively, for development:
-
Optional Dependencies: To use the
--copyfeature, you need to installpyperclip:pip install pyperclip
The primary way to use PyAnalysis is via the command line.
python -m pyanalyzer <filepath> [options]Arguments:
filepath: Path to the Python file (.py) to analyze.
Options:
-o FILE,--output FILE: Path to write the JSON output file. If omitted, prints to stdout.--copy: Copy the generated JSON report to the clipboard (requirespyperclip).--pretty/--no-pretty: Output formatted (pretty-printed) JSON (default) or compact JSON (--no-pretty).
Examples:
# Analyze a file and print pretty JSON to stdout
python -m pyanalyzer path/to/your_script.py
# Analyze a file and save compact JSON to report.json
python -m pyanalyzer my_module.py -o report.json --no-pretty
# Analyze a file, print pretty JSON to stdout, and copy it to the clipboard
python -m pyanalyzer script.py --copyYou can also use PyAnalysis programmatically within your Python scripts.
import json
from pyanalyzer import analyze_py_file, generate_json_report
filepath = "path/to/your_module.py"
try:
# 1. Analyze the file
analyzer_instance = analyze_py_file(filepath)
# 2. Generate the report dictionary
# The generate_json_report function handles sorting and cleaning None values
report_data = generate_json_report(analyzer_instance, filepath)
# 3. Process the report (e.g., print as JSON)
if report_data.get("analysis_error"):
print(f"Analysis Error: {report_data['analysis_error']}", file=sys.stderr)
# Handle error appropriately
json_output = json.dumps(report_data, indent=2, ensure_ascii=False)
print(json_output)
except Exception as e:
print(f"An unexpected error occurred: {e}")The tool outputs a JSON object containing the analysis results. Keys with None values are automatically removed from the output for clarity. Lists of variables, imports, etc., are sorted by line number and then by name for consistent output.
Top-Level Keys:
filepath: Absolute path to the analyzed file.analysis_error: String containing an error message if file/syntax/analysis errors occurred, otherwise the key is omitted.module_docstring: The docstring of the module, if present.imports: List ofimport xstatements. Each item hasname,alias,line.from_imports: Dictionary where keys are module names (.prefix for relative imports) and values are lists of imported items. Each item hasname,alias,line.constants: List of module-level variables identified as constants (SCREAMING_SNAKE_CASE). Each item hasname,type(if annotated),line.global_vars: List of other module-level variables. Each item hasname,type(if annotated),line.functions: Dictionary of top-level functions. Keys are function names. See Function/Class Structure below.classes: Dictionary of top-level classes. Keys are class names. See Function/Class Structure below.has_main_block: Boolean indicating if anif __name__ == "__main__":block was detected.
Function/Class Structure (Simplified):
Items within functions and classes (and their nested counterparts like methods, nested_functions, nested_classes) follow a similar structure:
{
"name": "function_or_class_name",
"line": 10,
"docstring": "Optional docstring...",
"decorators": ["@decorator1", ...],
// Function-specific:
"params": [
{"name": "arg1", "type": "int", "default": null, "kind": "POSITIONAL_OR_KEYWORD"},
{"name": "arg2", "type": "str", "default": "'default'", "kind": "POSITIONAL_OR_KEYWORD"},
// ... other parameter kinds
],
"return_type": "Optional[str]",
"local_vars": [...], // Variables defined inside
"nested_functions": {...},
"nested_classes": {...},
// Class-specific:
"base_classes": ["ParentClass", ...],
"methods": { // Includes instance, class, static methods, properties
"my_method": { ... /* recursive structure */ }
},
"class_vars": [...],
"instance_vars": [...] // Variables assigned via self.x or cls.x
}Example JSON Output (Snippet):
{
"filepath": "/path/to/example.py",
"module_docstring": "An example module.",
"has_main_block": true,
"imports": [
{
"name": "os",
"line": 3
}
],
"from_imports": {
"typing": [
{
"name": "List",
"line": 4
},
{
"name": "Optional",
"line": 4
}
]
},
"constants": [
{
"name": "MAX_RETRIES",
"type": "int",
"line": 6
}
],
"functions": {
"process_data": {
"name": "process_data",
"line": 9,
"docstring": "Processes the input data.",
"params": [
{
"name": "data",
"type": "List[str]",
"kind": "POSITIONAL_OR_KEYWORD"
},
{
"name": "retries",
"type": "int",
"default": "MAX_RETRIES",
"kind": "POSITIONAL_OR_KEYWORD"
}
],
"return_type": "Optional[bool]",
"decorators": [],
"local_vars": [
{
"name": "result",
"line": 11
}
],
"nested_functions": {},
"nested_classes": {}
}
},
"classes": {
"MyClass": {
"name": "MyClass",
"line": 15,
"docstring": "An example class.",
"base_classes": [],
"decorators": [],
"methods": {
"__init__": { ... },
"get_value": { ... }
},
"class_vars": [
{ "name": "default_name", "type": "str", "line": 16 }
],
"instance_vars": [
{ "name": "value", "type": "int", "line": 20 }
],
"nested_classes": {}
}
}
// analysis_error would be here if something went wrong
}Contributions are welcome! If you find a bug or have a feature request, please open an issue on the GitHub repository. If you'd like to contribute code:
- Fork the repository.
- Create a new branch (
git checkout -b feature/your-feature-name). - Make your changes.
- Add tests for your changes (if applicable).
- Ensure tests pass and code meets quality standards.
- Commit your changes (
git commit -am 'Add some feature'). - Push to the branch (
git push origin feature/your-feature-name). - Open a Pull Request.
This project is licensed under the MIT License.