check-duplicate-python-functions 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- check_duplicate_python_functions-1.0.0/CHANGELOG.md +58 -0
- check_duplicate_python_functions-1.0.0/MANIFEST.in +4 -0
- check_duplicate_python_functions-1.0.0/PKG-INFO +182 -0
- check_duplicate_python_functions-1.0.0/README.md +150 -0
- check_duplicate_python_functions-1.0.0/check_duplicate_functions.py +377 -0
- check_duplicate_python_functions-1.0.0/check_duplicate_python_functions.egg-info/PKG-INFO +182 -0
- check_duplicate_python_functions-1.0.0/check_duplicate_python_functions.egg-info/SOURCES.txt +14 -0
- check_duplicate_python_functions-1.0.0/check_duplicate_python_functions.egg-info/dependency_links.txt +1 -0
- check_duplicate_python_functions-1.0.0/check_duplicate_python_functions.egg-info/entry_points.txt +2 -0
- check_duplicate_python_functions-1.0.0/check_duplicate_python_functions.egg-info/top_level.txt +1 -0
- check_duplicate_python_functions-1.0.0/pyproject.toml +53 -0
- check_duplicate_python_functions-1.0.0/setup.cfg +10 -0
- check_duplicate_python_functions-1.0.0/setup.py +57 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Comprehensive code comments and docstrings throughout the codebase
|
|
12
|
+
- Detailed README.md with usage instructions, features, and examples
|
|
13
|
+
- CHANGELOG.md to track project changes
|
|
14
|
+
- PyPI packaging support with `setup.py` and `pyproject.toml`
|
|
15
|
+
- `MANIFEST.in` for including non-Python files in distribution
|
|
16
|
+
- `.gitignore` for build artifacts and development files
|
|
17
|
+
- `PYPI_UPLOAD.md` guide for publishing to PyPI
|
|
18
|
+
- Version number (`__version__ = "1.0.0"`) in main module
|
|
19
|
+
- Console script entry point: `check-duplicate-functions` command
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Enhanced docstrings for all classes and functions with detailed parameter descriptions
|
|
23
|
+
- Improved inline comments explaining complex logic
|
|
24
|
+
- Updated module-level docstring with comprehensive usage information
|
|
25
|
+
- Enhanced error messages and documentation
|
|
26
|
+
- Updated README.md with PyPI installation instructions
|
|
27
|
+
- Added multiple installation options (PyPI, git clone, direct download)
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
- Added comprehensive README.md covering:
|
|
31
|
+
- Features and capabilities
|
|
32
|
+
- Installation and usage instructions (including PyPI)
|
|
33
|
+
- Output format and exit codes
|
|
34
|
+
- How the tool works internally
|
|
35
|
+
- Special method handling
|
|
36
|
+
- Limitations and error handling
|
|
37
|
+
- Example use cases
|
|
38
|
+
- Added PyPI upload guide with step-by-step instructions
|
|
39
|
+
|
|
40
|
+
## [1.0.0] - Initial Release
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- Initial implementation of duplicate function detection
|
|
44
|
+
- AST-based function analysis using Python's `ast` module
|
|
45
|
+
- Support for regular and async function detection
|
|
46
|
+
- Class context tracking for methods
|
|
47
|
+
- Special method exclusion (dunder methods)
|
|
48
|
+
- Function signature extraction with type annotation support
|
|
49
|
+
- Error handling for file operations and syntax errors
|
|
50
|
+
- Command-line interface
|
|
51
|
+
|
|
52
|
+
### Features
|
|
53
|
+
- Detects duplicate function names in Python files
|
|
54
|
+
- Handles async functions
|
|
55
|
+
- Tracks class context for methods
|
|
56
|
+
- Excludes special methods from duplicate checks
|
|
57
|
+
- Extracts and compares function signatures
|
|
58
|
+
- Comprehensive error handling
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: check-duplicate-python-functions
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python tool to detect duplicate function names in Python source files using AST parsing
|
|
5
|
+
Home-page: https://github.com/pandiyarajk/check-duplicate-python-functions
|
|
6
|
+
Author: Pandiyaraj Karuppasamy
|
|
7
|
+
Author-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
|
|
8
|
+
Maintainer-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
|
|
9
|
+
Project-URL: Homepage, https://github.com/pandiyarajk/check-duplicate-python-functions
|
|
10
|
+
Project-URL: Documentation, https://github.com/pandiyarajk/check-duplicate-python-functions#readme
|
|
11
|
+
Project-URL: Repository, https://github.com/pandiyarajk/check-duplicate-python-functions
|
|
12
|
+
Project-URL: Issues, https://github.com/pandiyarajk/check-duplicate-python-functions/issues
|
|
13
|
+
Project-URL: Changelog, https://github.com/pandiyarajk/check-duplicate-python-functions/blob/main/CHANGE_LOG.md
|
|
14
|
+
Keywords: python,ast,duplicate,functions,code-quality,static-analysis,linter
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
27
|
+
Requires-Python: >=3.7
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: home-page
|
|
31
|
+
Dynamic: requires-python
|
|
32
|
+
|
|
33
|
+
# check-duplicate-python-functions
|
|
34
|
+
|
|
35
|
+
A Python tool to detect duplicate function names in Python source files using AST (Abstract Syntax Tree) parsing.
|
|
36
|
+
|
|
37
|
+
**Repository**: [https://github.com/pandiyarajk/check-duplicate-python-functions](https://github.com/pandiyarajk/check-duplicate-python-functions)
|
|
38
|
+
|
|
39
|
+
**Author**: Pandiyaraj Karuppasamy (pandiyarajk@live.com)
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Detects duplicate function names**: Identifies functions with the same name defined multiple times in a file
|
|
44
|
+
- **AST-based analysis**: Uses Python's built-in AST module for accurate parsing
|
|
45
|
+
- **Handles async functions**: Properly identifies and distinguishes async functions
|
|
46
|
+
- **Class context awareness**: Tracks function context (module-level vs class methods)
|
|
47
|
+
- **Special method exclusion**: Excludes special methods (like `__init__`, `__str__`, etc.) from duplicate checks since they can legitimately appear in multiple classes
|
|
48
|
+
- **Signature extraction**: Extracts and compares function signatures including type annotations
|
|
49
|
+
- **Comprehensive error handling**: Handles file not found, permission errors, encoding issues, and syntax errors gracefully
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Python 3.6 or higher
|
|
54
|
+
- No external dependencies (uses only Python standard library)
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
### Option 1: Install from PyPI (Recommended)
|
|
59
|
+
```bash
|
|
60
|
+
pip install check-duplicate-python-functions
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
After installation, you can use the command-line tool:
|
|
64
|
+
```bash
|
|
65
|
+
check-duplicate-functions <python_file_path>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Option 2: Clone the repository
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/pandiyarajk/check-duplicate-python-functions.git
|
|
71
|
+
cd check-duplicate-python-functions
|
|
72
|
+
python check_duplicate_functions.py <python_file_path>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Option 3: Download directly
|
|
76
|
+
No installation required. Simply download the `check_duplicate_functions.py` file and run it:
|
|
77
|
+
```bash
|
|
78
|
+
python check_duplicate_functions.py <python_file_path>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
### If installed from PyPI:
|
|
84
|
+
```bash
|
|
85
|
+
check-duplicate-functions <python_file_path>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### If using the script directly:
|
|
89
|
+
```bash
|
|
90
|
+
python check_duplicate_functions.py <python_file_path>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Examples
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Check a single Python file (PyPI installation)
|
|
97
|
+
check-duplicate-functions my_script.py
|
|
98
|
+
|
|
99
|
+
# Check a file in a subdirectory (PyPI installation)
|
|
100
|
+
check-duplicate-functions src/utils.py
|
|
101
|
+
|
|
102
|
+
# Using the script directly
|
|
103
|
+
python check_duplicate_functions.py my_script.py
|
|
104
|
+
python check_duplicate_functions.py src/utils.py
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Output
|
|
108
|
+
|
|
109
|
+
The script produces output only when duplicates are found or errors occur:
|
|
110
|
+
|
|
111
|
+
- **No duplicates**: Script exits silently with exit code 0
|
|
112
|
+
- **Duplicates found**: Prints each duplicate function name and the line numbers where it appears:
|
|
113
|
+
```
|
|
114
|
+
[DUPLICATE] 'my_function' appears on lines: 10, 25, 42
|
|
115
|
+
```
|
|
116
|
+
- **Errors**: Prints error messages for file not found, syntax errors, etc.:
|
|
117
|
+
```
|
|
118
|
+
[ERROR] File not found: nonexistent.py
|
|
119
|
+
[ERROR] Syntax error in file.py: invalid syntax (line 5)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Exit Codes
|
|
123
|
+
|
|
124
|
+
- `0`: Success (no duplicates found or analysis completed successfully)
|
|
125
|
+
- `1`: Error (invalid arguments, file not found, syntax error, or other issues)
|
|
126
|
+
|
|
127
|
+
## How It Works
|
|
128
|
+
|
|
129
|
+
1. **File Reading**: Reads the Python source file with UTF-8 encoding
|
|
130
|
+
2. **AST Parsing**: Parses the source code into an Abstract Syntax Tree
|
|
131
|
+
3. **Function Extraction**: Traverses the AST to extract all function definitions:
|
|
132
|
+
- Regular functions (`def`)
|
|
133
|
+
- Async functions (`async def`)
|
|
134
|
+
- Methods within classes
|
|
135
|
+
- Function signatures with type annotations
|
|
136
|
+
4. **Duplicate Detection**: Identifies functions with duplicate names (excluding special methods)
|
|
137
|
+
5. **Result Reporting**: Prints duplicate function names and their line numbers
|
|
138
|
+
|
|
139
|
+
## Special Methods
|
|
140
|
+
|
|
141
|
+
Special methods (dunder methods) like `__init__`, `__str__`, `__repr__`, etc. are excluded from duplicate checking because:
|
|
142
|
+
- They are expected to appear in multiple classes
|
|
143
|
+
- They serve specific purposes in Python's object model
|
|
144
|
+
- Having them in multiple classes is not considered a code quality issue
|
|
145
|
+
|
|
146
|
+
## Limitations
|
|
147
|
+
|
|
148
|
+
- Only analyzes a single file at a time (no recursive directory scanning)
|
|
149
|
+
- Does not detect duplicates across different files
|
|
150
|
+
- Special methods are excluded from duplicate detection
|
|
151
|
+
- Functions with identical names but different signatures are still reported as duplicates
|
|
152
|
+
|
|
153
|
+
## Error Handling
|
|
154
|
+
|
|
155
|
+
The script handles various error conditions:
|
|
156
|
+
|
|
157
|
+
- **File not found**: Returns appropriate error message
|
|
158
|
+
- **Permission denied**: Reports permission errors
|
|
159
|
+
- **Encoding errors**: Handles files with non-UTF-8 encoding issues
|
|
160
|
+
- **Syntax errors**: Reports Python syntax errors with line information
|
|
161
|
+
- **Unexpected errors**: Catches and reports any unexpected exceptions
|
|
162
|
+
|
|
163
|
+
## Example Use Cases
|
|
164
|
+
|
|
165
|
+
- **Code review**: Quickly identify potential duplicate function definitions before code review
|
|
166
|
+
- **Refactoring**: Find duplicate functions that might need to be consolidated
|
|
167
|
+
- **Code quality**: Maintain clean codebase by detecting accidental function name reuse
|
|
168
|
+
- **Large codebases**: Analyze individual files in large projects for duplicate functions
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
This project is provided as-is for use in detecting duplicate functions in Python code.
|
|
173
|
+
|
|
174
|
+
## Contributing
|
|
175
|
+
|
|
176
|
+
Contributions are welcome! Please feel free to submit issues or pull requests on [GitHub](https://github.com/pandiyarajk/check-duplicate-python-functions).
|
|
177
|
+
|
|
178
|
+
## Author
|
|
179
|
+
|
|
180
|
+
**Pandiyaraj Karuppasamy**
|
|
181
|
+
- Email: pandiyarajk@live.com
|
|
182
|
+
- GitHub: [@pandiyarajk](https://github.com/pandiyarajk)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# check-duplicate-python-functions
|
|
2
|
+
|
|
3
|
+
A Python tool to detect duplicate function names in Python source files using AST (Abstract Syntax Tree) parsing.
|
|
4
|
+
|
|
5
|
+
**Repository**: [https://github.com/pandiyarajk/check-duplicate-python-functions](https://github.com/pandiyarajk/check-duplicate-python-functions)
|
|
6
|
+
|
|
7
|
+
**Author**: Pandiyaraj Karuppasamy (pandiyarajk@live.com)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Detects duplicate function names**: Identifies functions with the same name defined multiple times in a file
|
|
12
|
+
- **AST-based analysis**: Uses Python's built-in AST module for accurate parsing
|
|
13
|
+
- **Handles async functions**: Properly identifies and distinguishes async functions
|
|
14
|
+
- **Class context awareness**: Tracks function context (module-level vs class methods)
|
|
15
|
+
- **Special method exclusion**: Excludes special methods (like `__init__`, `__str__`, etc.) from duplicate checks since they can legitimately appear in multiple classes
|
|
16
|
+
- **Signature extraction**: Extracts and compares function signatures including type annotations
|
|
17
|
+
- **Comprehensive error handling**: Handles file not found, permission errors, encoding issues, and syntax errors gracefully
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Python 3.6 or higher
|
|
22
|
+
- No external dependencies (uses only Python standard library)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
### Option 1: Install from PyPI (Recommended)
|
|
27
|
+
```bash
|
|
28
|
+
pip install check-duplicate-python-functions
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
After installation, you can use the command-line tool:
|
|
32
|
+
```bash
|
|
33
|
+
check-duplicate-functions <python_file_path>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Option 2: Clone the repository
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/pandiyarajk/check-duplicate-python-functions.git
|
|
39
|
+
cd check-duplicate-python-functions
|
|
40
|
+
python check_duplicate_functions.py <python_file_path>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Option 3: Download directly
|
|
44
|
+
No installation required. Simply download the `check_duplicate_functions.py` file and run it:
|
|
45
|
+
```bash
|
|
46
|
+
python check_duplicate_functions.py <python_file_path>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### If installed from PyPI:
|
|
52
|
+
```bash
|
|
53
|
+
check-duplicate-functions <python_file_path>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### If using the script directly:
|
|
57
|
+
```bash
|
|
58
|
+
python check_duplicate_functions.py <python_file_path>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Check a single Python file (PyPI installation)
|
|
65
|
+
check-duplicate-functions my_script.py
|
|
66
|
+
|
|
67
|
+
# Check a file in a subdirectory (PyPI installation)
|
|
68
|
+
check-duplicate-functions src/utils.py
|
|
69
|
+
|
|
70
|
+
# Using the script directly
|
|
71
|
+
python check_duplicate_functions.py my_script.py
|
|
72
|
+
python check_duplicate_functions.py src/utils.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Output
|
|
76
|
+
|
|
77
|
+
The script produces output only when duplicates are found or errors occur:
|
|
78
|
+
|
|
79
|
+
- **No duplicates**: Script exits silently with exit code 0
|
|
80
|
+
- **Duplicates found**: Prints each duplicate function name and the line numbers where it appears:
|
|
81
|
+
```
|
|
82
|
+
[DUPLICATE] 'my_function' appears on lines: 10, 25, 42
|
|
83
|
+
```
|
|
84
|
+
- **Errors**: Prints error messages for file not found, syntax errors, etc.:
|
|
85
|
+
```
|
|
86
|
+
[ERROR] File not found: nonexistent.py
|
|
87
|
+
[ERROR] Syntax error in file.py: invalid syntax (line 5)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Exit Codes
|
|
91
|
+
|
|
92
|
+
- `0`: Success (no duplicates found or analysis completed successfully)
|
|
93
|
+
- `1`: Error (invalid arguments, file not found, syntax error, or other issues)
|
|
94
|
+
|
|
95
|
+
## How It Works
|
|
96
|
+
|
|
97
|
+
1. **File Reading**: Reads the Python source file with UTF-8 encoding
|
|
98
|
+
2. **AST Parsing**: Parses the source code into an Abstract Syntax Tree
|
|
99
|
+
3. **Function Extraction**: Traverses the AST to extract all function definitions:
|
|
100
|
+
- Regular functions (`def`)
|
|
101
|
+
- Async functions (`async def`)
|
|
102
|
+
- Methods within classes
|
|
103
|
+
- Function signatures with type annotations
|
|
104
|
+
4. **Duplicate Detection**: Identifies functions with duplicate names (excluding special methods)
|
|
105
|
+
5. **Result Reporting**: Prints duplicate function names and their line numbers
|
|
106
|
+
|
|
107
|
+
## Special Methods
|
|
108
|
+
|
|
109
|
+
Special methods (dunder methods) like `__init__`, `__str__`, `__repr__`, etc. are excluded from duplicate checking because:
|
|
110
|
+
- They are expected to appear in multiple classes
|
|
111
|
+
- They serve specific purposes in Python's object model
|
|
112
|
+
- Having them in multiple classes is not considered a code quality issue
|
|
113
|
+
|
|
114
|
+
## Limitations
|
|
115
|
+
|
|
116
|
+
- Only analyzes a single file at a time (no recursive directory scanning)
|
|
117
|
+
- Does not detect duplicates across different files
|
|
118
|
+
- Special methods are excluded from duplicate detection
|
|
119
|
+
- Functions with identical names but different signatures are still reported as duplicates
|
|
120
|
+
|
|
121
|
+
## Error Handling
|
|
122
|
+
|
|
123
|
+
The script handles various error conditions:
|
|
124
|
+
|
|
125
|
+
- **File not found**: Returns appropriate error message
|
|
126
|
+
- **Permission denied**: Reports permission errors
|
|
127
|
+
- **Encoding errors**: Handles files with non-UTF-8 encoding issues
|
|
128
|
+
- **Syntax errors**: Reports Python syntax errors with line information
|
|
129
|
+
- **Unexpected errors**: Catches and reports any unexpected exceptions
|
|
130
|
+
|
|
131
|
+
## Example Use Cases
|
|
132
|
+
|
|
133
|
+
- **Code review**: Quickly identify potential duplicate function definitions before code review
|
|
134
|
+
- **Refactoring**: Find duplicate functions that might need to be consolidated
|
|
135
|
+
- **Code quality**: Maintain clean codebase by detecting accidental function name reuse
|
|
136
|
+
- **Large codebases**: Analyze individual files in large projects for duplicate functions
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
This project is provided as-is for use in detecting duplicate functions in Python code.
|
|
141
|
+
|
|
142
|
+
## Contributing
|
|
143
|
+
|
|
144
|
+
Contributions are welcome! Please feel free to submit issues or pull requests on [GitHub](https://github.com/pandiyarajk/check-duplicate-python-functions).
|
|
145
|
+
|
|
146
|
+
## Author
|
|
147
|
+
|
|
148
|
+
**Pandiyaraj Karuppasamy**
|
|
149
|
+
- Email: pandiyarajk@live.com
|
|
150
|
+
- GitHub: [@pandiyarajk](https://github.com/pandiyarajk)
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Python script to detect duplicate function names in Python files.
|
|
4
|
+
|
|
5
|
+
This script analyzes Python source files using AST (Abstract Syntax Tree) parsing
|
|
6
|
+
to identify duplicate function definitions. It can detect:
|
|
7
|
+
- Duplicate function names at module level
|
|
8
|
+
- Duplicate method names within classes
|
|
9
|
+
- Functions with identical signatures (potential code duplication)
|
|
10
|
+
- Special methods (like __init__, __str__, etc.) are excluded from duplicate checks
|
|
11
|
+
as they can legitimately appear in multiple classes
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
python check_duplicate_functions.py <python_file_path>
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
python check_duplicate_functions.py my_script.py
|
|
18
|
+
|
|
19
|
+
Exit codes:
|
|
20
|
+
0 - Success (no duplicates found or script completed successfully)
|
|
21
|
+
1 - Error (file not found, syntax error, or other issues)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
__version__ = "1.0.0"
|
|
25
|
+
|
|
26
|
+
import ast
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
from collections import Counter, defaultdict
|
|
30
|
+
from typing import Dict
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FunctionAnalyzer(ast.NodeVisitor):
|
|
34
|
+
"""
|
|
35
|
+
AST visitor to extract function definitions from Python code.
|
|
36
|
+
|
|
37
|
+
This class traverses the Abstract Syntax Tree of a Python file to collect
|
|
38
|
+
information about all function definitions, including their names, line numbers,
|
|
39
|
+
signatures, and class context.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
functions: List of tuples containing function information:
|
|
43
|
+
(name, line_number, signature, class_context, is_method, is_special_method)
|
|
44
|
+
function_lines: Dictionary mapping function names to lists of line numbers
|
|
45
|
+
where they are defined (excludes special methods)
|
|
46
|
+
function_signatures: Dictionary mapping function names to lists of their
|
|
47
|
+
signatures (excludes special methods)
|
|
48
|
+
class_stack: Stack to track the current class context during AST traversal
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self):
|
|
52
|
+
"""Initialize the FunctionAnalyzer with empty data structures."""
|
|
53
|
+
self.functions = []
|
|
54
|
+
# function_name -> list of line numbers where function is defined
|
|
55
|
+
self.function_lines = {}
|
|
56
|
+
# function_name -> list of signatures (for detecting identical signatures)
|
|
57
|
+
self.function_signatures = {}
|
|
58
|
+
# Track current class context during AST traversal
|
|
59
|
+
self.class_stack = []
|
|
60
|
+
|
|
61
|
+
def visit_FunctionDef(self, node):
|
|
62
|
+
"""
|
|
63
|
+
Visit regular (synchronous) function definitions.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
node: AST node representing a function definition
|
|
67
|
+
"""
|
|
68
|
+
self._add_function(node.name, node.lineno, self._get_signature(node))
|
|
69
|
+
self.generic_visit(node)
|
|
70
|
+
|
|
71
|
+
def visit_AsyncFunctionDef(self, node):
|
|
72
|
+
"""
|
|
73
|
+
Visit async function definitions.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
node: AST node representing an async function definition
|
|
77
|
+
"""
|
|
78
|
+
self._add_function(f"async {node.name}", node.lineno, self._get_signature(node))
|
|
79
|
+
self.generic_visit(node)
|
|
80
|
+
|
|
81
|
+
def visit_ClassDef(self, node):
|
|
82
|
+
"""
|
|
83
|
+
Visit class definitions to track class context for methods.
|
|
84
|
+
|
|
85
|
+
This method maintains a stack of class names to provide context
|
|
86
|
+
for nested class definitions and their methods.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
node: AST node representing a class definition
|
|
90
|
+
"""
|
|
91
|
+
self.class_stack.append(node.name)
|
|
92
|
+
self.generic_visit(node)
|
|
93
|
+
self.class_stack.pop()
|
|
94
|
+
|
|
95
|
+
def _add_function(self, name: str, lineno: int, signature: str):
|
|
96
|
+
"""
|
|
97
|
+
Add function to the tracking lists with context information.
|
|
98
|
+
|
|
99
|
+
This method stores function information including whether it's a method,
|
|
100
|
+
whether it's a special method (like __init__), and its class context.
|
|
101
|
+
Special methods are excluded from duplicate checking since they can
|
|
102
|
+
legitimately appear in multiple classes.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
name: Function name (may include "async " prefix for async functions)
|
|
106
|
+
lineno: Line number where the function is defined
|
|
107
|
+
signature: String representation of the function signature
|
|
108
|
+
"""
|
|
109
|
+
# Determine if function is defined within a class (method)
|
|
110
|
+
class_context = self.class_stack[-1] if self.class_stack else None
|
|
111
|
+
is_method = class_context is not None
|
|
112
|
+
# Check if this is a special method (dunder method)
|
|
113
|
+
is_special_method = name.startswith("__") and name.endswith("__")
|
|
114
|
+
|
|
115
|
+
# Store complete function information
|
|
116
|
+
func_info = (
|
|
117
|
+
name,
|
|
118
|
+
lineno,
|
|
119
|
+
signature,
|
|
120
|
+
class_context,
|
|
121
|
+
is_method,
|
|
122
|
+
is_special_method,
|
|
123
|
+
)
|
|
124
|
+
self.functions.append(func_info)
|
|
125
|
+
|
|
126
|
+
# For duplicate checking, exclude special methods like __init__, __str__, etc.
|
|
127
|
+
# since they can legitimately appear in multiple classes
|
|
128
|
+
if not is_special_method:
|
|
129
|
+
# Track line numbers for each function name
|
|
130
|
+
if name not in self.function_lines:
|
|
131
|
+
self.function_lines[name] = []
|
|
132
|
+
self.function_lines[name].append(lineno)
|
|
133
|
+
|
|
134
|
+
# Track signatures for detecting identical function signatures
|
|
135
|
+
if name not in self.function_signatures:
|
|
136
|
+
self.function_signatures[name] = []
|
|
137
|
+
self.function_signatures[name].append(signature)
|
|
138
|
+
|
|
139
|
+
def _get_signature(self, node) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Extract function signature as a string representation.
|
|
142
|
+
|
|
143
|
+
This method constructs a complete function signature including:
|
|
144
|
+
- Regular arguments with optional type annotations
|
|
145
|
+
- *args (variable positional arguments)
|
|
146
|
+
- **kwargs (variable keyword arguments)
|
|
147
|
+
- Keyword-only arguments
|
|
148
|
+
- Return type annotation
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
node: AST node representing a function definition
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
String representation of the function signature, e.g.:
|
|
155
|
+
"(arg1: str, arg2: int, *args, **kwargs) -> bool"
|
|
156
|
+
"""
|
|
157
|
+
args = []
|
|
158
|
+
|
|
159
|
+
# Process regular positional arguments
|
|
160
|
+
if node.args.args:
|
|
161
|
+
for arg in node.args.args:
|
|
162
|
+
arg_str = arg.arg
|
|
163
|
+
if arg.annotation:
|
|
164
|
+
arg_str += f": {self._get_annotation_name(arg.annotation)}"
|
|
165
|
+
args.append(arg_str)
|
|
166
|
+
|
|
167
|
+
# Handle *args (variable positional arguments)
|
|
168
|
+
if node.args.vararg:
|
|
169
|
+
vararg_str = f"*{node.args.vararg.arg}"
|
|
170
|
+
if node.args.vararg.annotation:
|
|
171
|
+
vararg_str += f": {self._get_annotation_name(node.args.vararg.annotation)}"
|
|
172
|
+
args.append(vararg_str)
|
|
173
|
+
|
|
174
|
+
# Handle **kwargs (variable keyword arguments)
|
|
175
|
+
if node.args.kwarg:
|
|
176
|
+
kwarg_str = f"**{node.args.kwarg.arg}"
|
|
177
|
+
if node.args.kwarg.annotation:
|
|
178
|
+
kwarg_str += f": {self._get_annotation_name(node.args.kwarg.annotation)}"
|
|
179
|
+
args.append(kwarg_str)
|
|
180
|
+
|
|
181
|
+
# Handle keyword-only arguments (arguments after * or *args)
|
|
182
|
+
if node.args.kwonlyargs:
|
|
183
|
+
for arg in node.args.kwonlyargs:
|
|
184
|
+
arg_str = arg.arg
|
|
185
|
+
if arg.annotation:
|
|
186
|
+
arg_str += f": {self._get_annotation_name(arg.annotation)}"
|
|
187
|
+
args.append(arg_str)
|
|
188
|
+
|
|
189
|
+
signature = f"({', '.join(args)})"
|
|
190
|
+
|
|
191
|
+
# Add return type annotation if present
|
|
192
|
+
if node.returns:
|
|
193
|
+
signature += f" -> {self._get_annotation_name(node.returns)}"
|
|
194
|
+
|
|
195
|
+
return signature
|
|
196
|
+
|
|
197
|
+
def _get_annotation_name(self, annotation) -> str:
|
|
198
|
+
"""
|
|
199
|
+
Convert AST annotation node to string representation.
|
|
200
|
+
|
|
201
|
+
This method recursively processes type annotations from the AST,
|
|
202
|
+
handling various annotation types including:
|
|
203
|
+
- Simple names (e.g., 'str', 'int')
|
|
204
|
+
- Attribute access (e.g., 'typing.List', 'collections.abc.Iterable')
|
|
205
|
+
- Subscripts (e.g., 'List[str]', 'Dict[str, int]')
|
|
206
|
+
- Literal types (e.g., 'Literal["value"]')
|
|
207
|
+
- Compatibility with older Python versions (ast.Index)
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
annotation: AST node representing a type annotation
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
String representation of the type annotation
|
|
214
|
+
"""
|
|
215
|
+
if isinstance(annotation, ast.Name):
|
|
216
|
+
# Simple type name like 'str', 'int', 'bool'
|
|
217
|
+
return annotation.id
|
|
218
|
+
elif isinstance(annotation, ast.Attribute):
|
|
219
|
+
# Attribute access like 'typing.List', 'collections.abc.Iterable'
|
|
220
|
+
return f"{self._get_annotation_name(annotation.value)}.{annotation.attr}"
|
|
221
|
+
elif isinstance(annotation, ast.Subscript):
|
|
222
|
+
# Generic types like 'List[str]', 'Dict[str, int]'
|
|
223
|
+
return f"{self._get_annotation_name(annotation.value)}[{self._get_annotation_name(annotation.slice)}]"
|
|
224
|
+
elif isinstance(annotation, ast.Index):
|
|
225
|
+
# Compatibility with older Python versions (< 3.9)
|
|
226
|
+
return self._get_annotation_name(annotation.value)
|
|
227
|
+
elif isinstance(annotation, ast.Constant):
|
|
228
|
+
# Literal types and constants
|
|
229
|
+
return repr(annotation.value)
|
|
230
|
+
else:
|
|
231
|
+
# Fallback for any other annotation types
|
|
232
|
+
return str(annotation)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def analyze_file(file_path: str) -> Dict:
|
|
236
|
+
"""
|
|
237
|
+
Analyze a Python file for duplicate function names.
|
|
238
|
+
|
|
239
|
+
This function reads a Python file, parses it into an AST, and analyzes
|
|
240
|
+
all function definitions to identify duplicates. It handles various
|
|
241
|
+
error cases including file not found, permission errors, encoding issues,
|
|
242
|
+
and syntax errors.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
file_path: Path to the Python file to analyze
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Dictionary containing analysis results with the following keys:
|
|
249
|
+
- 'duplicates': Dictionary mapping function names to sorted lists of
|
|
250
|
+
line numbers where duplicates are found (excludes special methods)
|
|
251
|
+
- 'all_functions': List of tuples containing all function information:
|
|
252
|
+
(name, line_number, signature, class_context, is_method, is_special_method)
|
|
253
|
+
- 'duplicate_signatures': Dictionary mapping signatures to lists of
|
|
254
|
+
function names that share identical signatures
|
|
255
|
+
- 'total_functions': Total number of functions found (including special methods)
|
|
256
|
+
- 'unique_function_names': Number of unique function names (excluding special methods)
|
|
257
|
+
- 'special_methods': List of special methods (dunder methods) found
|
|
258
|
+
- 'error': Error message string (only present if an error occurred)
|
|
259
|
+
"""
|
|
260
|
+
# Read the source file with error handling
|
|
261
|
+
try:
|
|
262
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
263
|
+
source_code = f.read()
|
|
264
|
+
except FileNotFoundError:
|
|
265
|
+
return {"error": f"File not found: {file_path}"}
|
|
266
|
+
except PermissionError:
|
|
267
|
+
return {"error": f"Permission denied: {file_path}"}
|
|
268
|
+
except UnicodeDecodeError:
|
|
269
|
+
return {"error": f"Encoding error reading file: {file_path}"}
|
|
270
|
+
|
|
271
|
+
# Parse the source code into an AST
|
|
272
|
+
try:
|
|
273
|
+
tree = ast.parse(source_code, filename=file_path)
|
|
274
|
+
except SyntaxError as e:
|
|
275
|
+
return {"error": f"Syntax error in {file_path}: {e}"}
|
|
276
|
+
|
|
277
|
+
# Analyze the AST to extract function information
|
|
278
|
+
analyzer = FunctionAnalyzer()
|
|
279
|
+
analyzer.visit(tree)
|
|
280
|
+
|
|
281
|
+
# Find duplicate function names (functions with the same name defined multiple times)
|
|
282
|
+
duplicates = {}
|
|
283
|
+
for func_name, lines in analyzer.function_lines.items():
|
|
284
|
+
if len(lines) > 1:
|
|
285
|
+
duplicates[func_name] = sorted(lines)
|
|
286
|
+
|
|
287
|
+
# Find functions with identical signatures (potential code duplication or overloads)
|
|
288
|
+
duplicate_signatures = defaultdict(list)
|
|
289
|
+
for func_name, signatures in analyzer.function_signatures.items():
|
|
290
|
+
sig_counter = Counter(signatures)
|
|
291
|
+
for sig, count in sig_counter.items():
|
|
292
|
+
if count > 1:
|
|
293
|
+
duplicate_signatures[sig].extend([func_name] * count)
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
"duplicates": duplicates,
|
|
297
|
+
"all_functions": analyzer.functions,
|
|
298
|
+
"duplicate_signatures": dict(duplicate_signatures),
|
|
299
|
+
"total_functions": len(analyzer.functions),
|
|
300
|
+
"unique_function_names": len(analyzer.function_lines),
|
|
301
|
+
# Extract special methods (functions where is_special_method=True, index 5 in tuple)
|
|
302
|
+
"special_methods": [f for f in analyzer.functions if f[5]],
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def print_analysis_results(file_path: str, results: Dict):
|
|
307
|
+
"""
|
|
308
|
+
Print the analysis results in a readable format.
|
|
309
|
+
|
|
310
|
+
This function only prints output when duplicates are found or errors occur.
|
|
311
|
+
If no duplicates are found, the function produces no output (silent success).
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
file_path: Path to the analyzed file (for context in error messages)
|
|
315
|
+
results: Dictionary containing analysis results from analyze_file()
|
|
316
|
+
"""
|
|
317
|
+
# Print error messages if any occurred during analysis
|
|
318
|
+
if "error" in results:
|
|
319
|
+
print(f"[ERROR] {results['error']}")
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
# Print duplicate function names and their line numbers
|
|
323
|
+
duplicates = results.get("duplicates", {})
|
|
324
|
+
if duplicates:
|
|
325
|
+
for func_name, lines in duplicates.items():
|
|
326
|
+
print(f"[DUPLICATE] '{func_name}' appears on lines: {', '.join(map(str, lines))}")
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def main():
|
|
330
|
+
"""
|
|
331
|
+
Main function to handle command line arguments and orchestrate the analysis.
|
|
332
|
+
|
|
333
|
+
This function:
|
|
334
|
+
1. Validates command line arguments
|
|
335
|
+
2. Checks if the file exists
|
|
336
|
+
3. Warns if the file doesn't have a .py extension
|
|
337
|
+
4. Analyzes the file for duplicate functions
|
|
338
|
+
5. Prints the results
|
|
339
|
+
|
|
340
|
+
Exit codes:
|
|
341
|
+
0: Success (no duplicates found or analysis completed)
|
|
342
|
+
1: Error (invalid arguments, file not found, or analysis error)
|
|
343
|
+
"""
|
|
344
|
+
# Validate command line arguments
|
|
345
|
+
if len(sys.argv) != 2:
|
|
346
|
+
print("Usage: python check_duplicate_functions.py <python_file_path>")
|
|
347
|
+
print("Example: python check_duplicate_functions.py my_script.py")
|
|
348
|
+
sys.exit(1)
|
|
349
|
+
|
|
350
|
+
file_path = sys.argv[1]
|
|
351
|
+
|
|
352
|
+
# Check if file exists
|
|
353
|
+
if not os.path.isfile(file_path):
|
|
354
|
+
print(f"[ERROR] File not found: {file_path}")
|
|
355
|
+
sys.exit(1)
|
|
356
|
+
|
|
357
|
+
# Warn if file doesn't have .py extension (but continue anyway)
|
|
358
|
+
if not file_path.endswith(".py"):
|
|
359
|
+
print(f"[WARNING] File doesn't have .py extension: {file_path}")
|
|
360
|
+
|
|
361
|
+
# Perform analysis and print results
|
|
362
|
+
results = analyze_file(file_path)
|
|
363
|
+
print_analysis_results(file_path, results)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
if __name__ == "__main__":
|
|
367
|
+
"""
|
|
368
|
+
Entry point for the script when run directly.
|
|
369
|
+
|
|
370
|
+
Wraps main() in a try-except block to handle any unexpected errors
|
|
371
|
+
and ensure proper exit codes.
|
|
372
|
+
"""
|
|
373
|
+
try:
|
|
374
|
+
main()
|
|
375
|
+
except Exception as e:
|
|
376
|
+
print(f"[ERROR] Unexpected error: {str(e)}")
|
|
377
|
+
sys.exit(1)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: check-duplicate-python-functions
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python tool to detect duplicate function names in Python source files using AST parsing
|
|
5
|
+
Home-page: https://github.com/pandiyarajk/check-duplicate-python-functions
|
|
6
|
+
Author: Pandiyaraj Karuppasamy
|
|
7
|
+
Author-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
|
|
8
|
+
Maintainer-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
|
|
9
|
+
Project-URL: Homepage, https://github.com/pandiyarajk/check-duplicate-python-functions
|
|
10
|
+
Project-URL: Documentation, https://github.com/pandiyarajk/check-duplicate-python-functions#readme
|
|
11
|
+
Project-URL: Repository, https://github.com/pandiyarajk/check-duplicate-python-functions
|
|
12
|
+
Project-URL: Issues, https://github.com/pandiyarajk/check-duplicate-python-functions/issues
|
|
13
|
+
Project-URL: Changelog, https://github.com/pandiyarajk/check-duplicate-python-functions/blob/main/CHANGE_LOG.md
|
|
14
|
+
Keywords: python,ast,duplicate,functions,code-quality,static-analysis,linter
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
27
|
+
Requires-Python: >=3.7
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: home-page
|
|
31
|
+
Dynamic: requires-python
|
|
32
|
+
|
|
33
|
+
# check-duplicate-python-functions
|
|
34
|
+
|
|
35
|
+
A Python tool to detect duplicate function names in Python source files using AST (Abstract Syntax Tree) parsing.
|
|
36
|
+
|
|
37
|
+
**Repository**: [https://github.com/pandiyarajk/check-duplicate-python-functions](https://github.com/pandiyarajk/check-duplicate-python-functions)
|
|
38
|
+
|
|
39
|
+
**Author**: Pandiyaraj Karuppasamy (pandiyarajk@live.com)
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Detects duplicate function names**: Identifies functions with the same name defined multiple times in a file
|
|
44
|
+
- **AST-based analysis**: Uses Python's built-in AST module for accurate parsing
|
|
45
|
+
- **Handles async functions**: Properly identifies and distinguishes async functions
|
|
46
|
+
- **Class context awareness**: Tracks function context (module-level vs class methods)
|
|
47
|
+
- **Special method exclusion**: Excludes special methods (like `__init__`, `__str__`, etc.) from duplicate checks since they can legitimately appear in multiple classes
|
|
48
|
+
- **Signature extraction**: Extracts and compares function signatures including type annotations
|
|
49
|
+
- **Comprehensive error handling**: Handles file not found, permission errors, encoding issues, and syntax errors gracefully
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Python 3.6 or higher
|
|
54
|
+
- No external dependencies (uses only Python standard library)
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
### Option 1: Install from PyPI (Recommended)
|
|
59
|
+
```bash
|
|
60
|
+
pip install check-duplicate-python-functions
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
After installation, you can use the command-line tool:
|
|
64
|
+
```bash
|
|
65
|
+
check-duplicate-functions <python_file_path>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Option 2: Clone the repository
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/pandiyarajk/check-duplicate-python-functions.git
|
|
71
|
+
cd check-duplicate-python-functions
|
|
72
|
+
python check_duplicate_functions.py <python_file_path>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Option 3: Download directly
|
|
76
|
+
No installation required. Simply download the `check_duplicate_functions.py` file and run it:
|
|
77
|
+
```bash
|
|
78
|
+
python check_duplicate_functions.py <python_file_path>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
### If installed from PyPI:
|
|
84
|
+
```bash
|
|
85
|
+
check-duplicate-functions <python_file_path>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### If using the script directly:
|
|
89
|
+
```bash
|
|
90
|
+
python check_duplicate_functions.py <python_file_path>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Examples
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Check a single Python file (PyPI installation)
|
|
97
|
+
check-duplicate-functions my_script.py
|
|
98
|
+
|
|
99
|
+
# Check a file in a subdirectory (PyPI installation)
|
|
100
|
+
check-duplicate-functions src/utils.py
|
|
101
|
+
|
|
102
|
+
# Using the script directly
|
|
103
|
+
python check_duplicate_functions.py my_script.py
|
|
104
|
+
python check_duplicate_functions.py src/utils.py
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Output
|
|
108
|
+
|
|
109
|
+
The script produces output only when duplicates are found or errors occur:
|
|
110
|
+
|
|
111
|
+
- **No duplicates**: Script exits silently with exit code 0
|
|
112
|
+
- **Duplicates found**: Prints each duplicate function name and the line numbers where it appears:
|
|
113
|
+
```
|
|
114
|
+
[DUPLICATE] 'my_function' appears on lines: 10, 25, 42
|
|
115
|
+
```
|
|
116
|
+
- **Errors**: Prints error messages for file not found, syntax errors, etc.:
|
|
117
|
+
```
|
|
118
|
+
[ERROR] File not found: nonexistent.py
|
|
119
|
+
[ERROR] Syntax error in file.py: invalid syntax (line 5)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Exit Codes
|
|
123
|
+
|
|
124
|
+
- `0`: Success (no duplicates found or analysis completed successfully)
|
|
125
|
+
- `1`: Error (invalid arguments, file not found, syntax error, or other issues)
|
|
126
|
+
|
|
127
|
+
## How It Works
|
|
128
|
+
|
|
129
|
+
1. **File Reading**: Reads the Python source file with UTF-8 encoding
|
|
130
|
+
2. **AST Parsing**: Parses the source code into an Abstract Syntax Tree
|
|
131
|
+
3. **Function Extraction**: Traverses the AST to extract all function definitions:
|
|
132
|
+
- Regular functions (`def`)
|
|
133
|
+
- Async functions (`async def`)
|
|
134
|
+
- Methods within classes
|
|
135
|
+
- Function signatures with type annotations
|
|
136
|
+
4. **Duplicate Detection**: Identifies functions with duplicate names (excluding special methods)
|
|
137
|
+
5. **Result Reporting**: Prints duplicate function names and their line numbers
|
|
138
|
+
|
|
139
|
+
## Special Methods
|
|
140
|
+
|
|
141
|
+
Special methods (dunder methods) like `__init__`, `__str__`, `__repr__`, etc. are excluded from duplicate checking because:
|
|
142
|
+
- They are expected to appear in multiple classes
|
|
143
|
+
- They serve specific purposes in Python's object model
|
|
144
|
+
- Having them in multiple classes is not considered a code quality issue
|
|
145
|
+
|
|
146
|
+
## Limitations
|
|
147
|
+
|
|
148
|
+
- Only analyzes a single file at a time (no recursive directory scanning)
|
|
149
|
+
- Does not detect duplicates across different files
|
|
150
|
+
- Special methods are excluded from duplicate detection
|
|
151
|
+
- Functions with identical names but different signatures are still reported as duplicates
|
|
152
|
+
|
|
153
|
+
## Error Handling
|
|
154
|
+
|
|
155
|
+
The script handles various error conditions:
|
|
156
|
+
|
|
157
|
+
- **File not found**: Returns appropriate error message
|
|
158
|
+
- **Permission denied**: Reports permission errors
|
|
159
|
+
- **Encoding errors**: Handles files with non-UTF-8 encoding issues
|
|
160
|
+
- **Syntax errors**: Reports Python syntax errors with line information
|
|
161
|
+
- **Unexpected errors**: Catches and reports any unexpected exceptions
|
|
162
|
+
|
|
163
|
+
## Example Use Cases
|
|
164
|
+
|
|
165
|
+
- **Code review**: Quickly identify potential duplicate function definitions before code review
|
|
166
|
+
- **Refactoring**: Find duplicate functions that might need to be consolidated
|
|
167
|
+
- **Code quality**: Maintain clean codebase by detecting accidental function name reuse
|
|
168
|
+
- **Large codebases**: Analyze individual files in large projects for duplicate functions
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
This project is provided as-is for use in detecting duplicate functions in Python code.
|
|
173
|
+
|
|
174
|
+
## Contributing
|
|
175
|
+
|
|
176
|
+
Contributions are welcome! Please feel free to submit issues or pull requests on [GitHub](https://github.com/pandiyarajk/check-duplicate-python-functions).
|
|
177
|
+
|
|
178
|
+
## Author
|
|
179
|
+
|
|
180
|
+
**Pandiyaraj Karuppasamy**
|
|
181
|
+
- Email: pandiyarajk@live.com
|
|
182
|
+
- GitHub: [@pandiyarajk](https://github.com/pandiyarajk)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
check_duplicate_functions.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
setup.cfg
|
|
7
|
+
setup.py
|
|
8
|
+
./check_duplicate_functions.py
|
|
9
|
+
./setup.py
|
|
10
|
+
check_duplicate_python_functions.egg-info/PKG-INFO
|
|
11
|
+
check_duplicate_python_functions.egg-info/SOURCES.txt
|
|
12
|
+
check_duplicate_python_functions.egg-info/dependency_links.txt
|
|
13
|
+
check_duplicate_python_functions.egg-info/entry_points.txt
|
|
14
|
+
check_duplicate_python_functions.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
check_duplicate_python_functions-1.0.0/check_duplicate_python_functions.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
check_duplicate_functions
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "check-duplicate-python-functions"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A Python tool to detect duplicate function names in Python source files using AST parsing"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Pandiyaraj Karuppasamy", email = "pandiyarajk@live.com"},
|
|
13
|
+
]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{name = "Pandiyaraj Karuppasamy", email = "pandiyarajk@live.com"},
|
|
16
|
+
]
|
|
17
|
+
keywords = [
|
|
18
|
+
"python",
|
|
19
|
+
"ast",
|
|
20
|
+
"duplicate",
|
|
21
|
+
"functions",
|
|
22
|
+
"code-quality",
|
|
23
|
+
"static-analysis",
|
|
24
|
+
"linter"
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 4 - Beta",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.7",
|
|
32
|
+
"Programming Language :: Python :: 3.8",
|
|
33
|
+
"Programming Language :: Python :: 3.9",
|
|
34
|
+
"Programming Language :: Python :: 3.10",
|
|
35
|
+
"Programming Language :: Python :: 3.11",
|
|
36
|
+
"Programming Language :: Python :: 3.12",
|
|
37
|
+
"Topic :: Software Development :: Testing",
|
|
38
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
39
|
+
]
|
|
40
|
+
dependencies = []
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
check-duplicate-functions = "check_duplicate_functions:main"
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/pandiyarajk/check-duplicate-python-functions"
|
|
47
|
+
Documentation = "https://github.com/pandiyarajk/check-duplicate-python-functions#readme"
|
|
48
|
+
Repository = "https://github.com/pandiyarajk/check-duplicate-python-functions"
|
|
49
|
+
Issues = "https://github.com/pandiyarajk/check-duplicate-python-functions/issues"
|
|
50
|
+
Changelog = "https://github.com/pandiyarajk/check-duplicate-python-functions/blob/main/CHANGE_LOG.md"
|
|
51
|
+
|
|
52
|
+
[tool.setuptools]
|
|
53
|
+
py-modules = ["check_duplicate_functions"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Setup configuration for check-duplicate-python-functions package."""
|
|
2
|
+
|
|
3
|
+
from setuptools import setup, find_packages
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# Read the README file
|
|
7
|
+
readme_file = Path(__file__).parent / "README.md"
|
|
8
|
+
long_description = readme_file.read_text(encoding="utf-8") if readme_file.exists() else ""
|
|
9
|
+
|
|
10
|
+
# Read version from the main module
|
|
11
|
+
version = {}
|
|
12
|
+
version_file = Path(__file__).parent / "check_duplicate_functions.py"
|
|
13
|
+
if version_file.exists():
|
|
14
|
+
with open(version_file, "r", encoding="utf-8") as f:
|
|
15
|
+
for line in f:
|
|
16
|
+
if line.startswith("__version__"):
|
|
17
|
+
exec(line, version)
|
|
18
|
+
break
|
|
19
|
+
|
|
20
|
+
setup(
|
|
21
|
+
name="check-duplicate-python-functions",
|
|
22
|
+
version=version.get("__version__", "1.0.0"),
|
|
23
|
+
author="Pandiyaraj Karuppasamy",
|
|
24
|
+
author_email="pandiyarajk@live.com",
|
|
25
|
+
description="A Python tool to detect duplicate function names in Python source files using AST parsing",
|
|
26
|
+
long_description=long_description,
|
|
27
|
+
long_description_content_type="text/markdown",
|
|
28
|
+
url="https://github.com/pandiyarajk/check-duplicate-python-functions",
|
|
29
|
+
py_modules=["check_duplicate_functions"],
|
|
30
|
+
classifiers=[
|
|
31
|
+
"Development Status :: 4 - Beta",
|
|
32
|
+
"Intended Audience :: Developers",
|
|
33
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
"License :: OSI Approved :: MIT License",
|
|
36
|
+
"Programming Language :: Python :: 3",
|
|
37
|
+
"Programming Language :: Python :: 3.7",
|
|
38
|
+
"Programming Language :: Python :: 3.8",
|
|
39
|
+
"Programming Language :: Python :: 3.9",
|
|
40
|
+
"Programming Language :: Python :: 3.10",
|
|
41
|
+
"Programming Language :: Python :: 3.11",
|
|
42
|
+
"Programming Language :: Python :: 3.12",
|
|
43
|
+
"Operating System :: OS Independent",
|
|
44
|
+
],
|
|
45
|
+
python_requires=">=3.7",
|
|
46
|
+
entry_points={
|
|
47
|
+
"console_scripts": [
|
|
48
|
+
"check-duplicate-functions=check_duplicate_functions:main",
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
keywords="python, ast, duplicate, functions, code-quality, static-analysis, linter",
|
|
52
|
+
project_urls={
|
|
53
|
+
"Bug Reports": "https://github.com/pandiyarajk/check-duplicate-python-functions/issues",
|
|
54
|
+
"Source": "https://github.com/pandiyarajk/check-duplicate-python-functions",
|
|
55
|
+
"Documentation": "https://github.com/pandiyarajk/check-duplicate-python-functions#readme",
|
|
56
|
+
},
|
|
57
|
+
)
|