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.
@@ -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,4 @@
1
+ include README.md
2
+ include CHANGELOG.md
3
+ include pyproject.toml
4
+ recursive-include . *.py
@@ -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,2 @@
1
+ [console_scripts]
2
+ check-duplicate-functions = check_duplicate_functions:main
@@ -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,10 @@
1
+ [metadata]
2
+ license_file =
3
+
4
+ [options]
5
+ py_modules = check_duplicate_functions
6
+
7
+ [egg_info]
8
+ tag_build =
9
+ tag_date = 0
10
+
@@ -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
+ )