A Python tool that analyzes Python codebases to find and optionally rename local-only symbols (functions and global variables that are not used outside their module).
This tool renames local functions and globals to start with an _.
It has helped me a lot in a medium sized stand-alone project with many module-level functions and globals.
The tool works at the moment for:
- global functions
- global variables
But it does not handle:
- class types (feel free to contribute)
- class methods
- class attributes
- class variables
(As far i can think classes are tricky to implement as python can be very dynamic).
- Symbol Analysis: Scans Python files to identify functions and global variables
- Reference Tracking: Finds external references to symbols across modules
- Selective Scanning: Scan only functions, only globals, or both
- Dry-run Mode: Preview planned renames without applying them
- Warning Mode: Get warnings for unused symbols
- Exit Codes: Proper exit codes for CI integration
# Clone the repository
git clone <repository-url>
cd local_reference_renamer
# Install dependencies using uv
uv sync
# Or install manually
pip install libcstScan a project to see all symbols and their external reference counts:
python local_reference_renamer.py --root /path/to/your/projectScan only functions:
python local_reference_renamer.py --root /path/to/your/project --funcsScan only global variables:
python local_reference_renamer.py --root /path/to/your/project --globalsPreview what renames would be applied without actually making changes:
python local_reference_renamer.py --root /path/to/your/project --rename-locals --dry-runActually rename local-only symbols (prefixes them with _):
python local_reference_renamer.py --root /path/to/your/project --rename-localsGet warnings for symbols with zero total references:
python local_reference_renamer.py --root /path/to/your/project --warn-unusedGet detailed information including file locations of references:
python local_reference_renamer.py --root /path/to/your/project --verboseScan only specific Python files instead of the entire project:
python local_reference_renamer.py --root /path/to/your/project src/main.py src/utils.pyThe tool outputs a table showing:
- Symbol: Name of the function or global variable
- Type:
ffor functions,gfor globals - Module: The file containing the symbol
- Count: Number of references from other modules
Example output:
+-------------------------+--------+------------+---------+
| Symbol | Type | Module | Count |
+=========================+========+============+=========+
| helper_func | f | helpers.py | 2 |
+-------------------------+--------+------------+---------+
| unused_function | f | unused.py | 0 |
+-------------------------+--------+------------+---------+
| GLOBAL_VAR | g | utils.py | 1 |
+-------------------------+--------+------------+---------+
| UNUSED_GLOBAL | g | unused.py | 0 |
+-------------------------+--------+------------+---------+
0: Unused symbols were found1: No unused symbols found
This makes the tool suitable for CI integration to detect unused code.
Examples
$ python local_reference_renamer.py --root ./my_project
+-------------------------+--------+------------+---------+
| Symbol | Type | Module | Count |
+=========================+========+============+=========+
| main | f | main.py | 0 |
+-------------------------+--------+------------+---------+
| helper_func | f | utils.py | 1 |
+-------------------------+--------+------------+---------+
| unused_function | f | unused.py | 0 |
+-------------------------+--------+------------+---------+
| GLOBAL_VAR | g | utils.py | 1 |
+-------------------------+--------+------------+---------+
| UNUSED_GLOBAL | g | unused.py | 0 |
+-------------------------+--------+------------+---------+$ python local_reference_renamer.py --root ./my_project --rename-locals --dry-run
+-------------------------+--------+------------+---------+
| Symbol | Type | Module | Count |
+=========================+========+============+=========+
| main | f | main.py | 0 |
+-------------------------+--------+------------+---------+
| helper_func | f | utils.py | 1 |
+-------------------------+--------+------------+---------+
| unused_function | f | unused.py | 0 |
+-------------------------+--------+------------+---------+
| GLOBAL_VAR | g | utils.py | 1 |
+-------------------------+--------+------------+---------+
| UNUSED_GLOBAL | g | unused.py | 0 |
+-------------------------+--------+------------+---------+
Planned renames:
- main -> _main in main.py
- unused_function -> _unused_function in unused.py
- UNUSED_GLOBAL -> _UNUSED_GLOBAL in unused.py$ python local_reference_renamer.py --root ./my_project --warn-unused
Unused: main in /path/to/my_project/main.py
Unused: unused_function in /path/to/my_project/unused.py
Unused: UNUSED_GLOBAL in /path/to/my_project/unused.py
+-------------------------+--------+------------+---------+
| Symbol | Type | Module | Count |
+=========================+========+============+=========+
| main | f | main.py | 0 |
+-------------------------+--------+------------+---------+
| helper_func | f | utils.py | 1 |
+-------------------------+--------+------------+---------+
| unused_function | f | unused.py | 0 |
+-------------------------+--------+------------+---------+
| GLOBAL_VAR | g | utils.py | 1 |
+-------------------------+--------+------------+---------+
| UNUSED_GLOBAL | g | unused.py | 0 |
+-------------------------+--------+------------+---------+The tool handles various Python constructs:
- Tuple assignments:
a, b = 1, 2 - Annotated assignments:
value: int = 10 if __name__ == '__main__'blocks- Import aliases
- Already prefixed symbols (skips
_private_func)
Testing
Run the test suite:
# Install test dependencies
uv sync --extra test
# Run tests
uv run python -m pytest tests/ -v
# Run with coverage
uv run python -m pytest tests/ --cov=local_reference_renamerThe project uses two types of regression testing:
Located in tests/test_projects/, these contain:
original/- Project files before renamingrenamed/- Expected project files after renaming
These test projects contain known patterns of public/private functions and variables to verify the tool works correctly.
The project also uses golden files testing against the HDL-FSM-Editor repository to ensure consistent behavior across changes. Golden files contain expected output from scanning the reference project.
Generate initial golden files:
# Run the golden file generation script
python tests/generate_golden_files.pyWhen you make changes that affect the tool's output format or behavior, update the golden files:
# Run tests with --update-golden flag
uv run python -m pytest tests/ -k "golden" --update-golden
# Or run the generation script
python tests/generate_golden_files.pyThe following files are maintained in tests/golden_files/:
golden_scan_output.txt- Expected output from scanning the golden projectgolden_dry_run_output.txt- Expected output from dry-run modegolden_commit_hash.txt- The commit hash of the golden project version being tested against
If tests fail, check:
- Import errors: Ensure all dependencies are installed
- Permission errors: Make sure you have write access to test directories
- Git clone failures: Check network connectivity for golden project tests
- Encoding issues: The tool uses ASCII arrows (
->) for compatibility
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
MIT License