complexipy 5.2.0__cp38-cp38-win_amd64.whl

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.
complexipy/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from complexipy._complexipy import (
4
+ CodeComplexity,
5
+ FileComplexity,
6
+ FunctionComplexity,
7
+ LineComplexity,
8
+ )
9
+ from complexipy.api import (
10
+ code_complexity,
11
+ file_complexity,
12
+ )
13
+
14
+ __all__ = [
15
+ "CodeComplexity",
16
+ "FileComplexity",
17
+ "FunctionComplexity",
18
+ "LineComplexity",
19
+ "code_complexity",
20
+ "file_complexity",
21
+ ]
@@ -0,0 +1,518 @@
1
+ """
2
+ Type stubs for complexipy._complexipy module.
3
+
4
+ This module provides Rust-powered cognitive complexity analysis for Python code.
5
+ Cognitive complexity is a metric that measures how difficult a function is to
6
+ understand and maintain, focusing on control flow structures that make code
7
+ harder to reason about.
8
+ """
9
+
10
+ from typing import List, Tuple
11
+
12
+ class LineComplexity:
13
+ """
14
+ Represents the cognitive complexity contribution of a single line of code.
15
+
16
+ Cognitive complexity is incremented by control flow structures like:
17
+ - if/elif statements (+1 each)
18
+ - loops (for/while) (+1 each)
19
+ - except clauses (+1 each)
20
+ - Nested structures (+1 for each level of nesting)
21
+ - Boolean operators in conditions (and/or) (+1 each)
22
+
23
+ This class tracks which specific lines contribute to the overall complexity
24
+ score, helping developers identify the most complex parts of their functions.
25
+
26
+ Example:
27
+ >>> # For a line like: if condition and other_condition:
28
+ >>> line_complexity = LineComplexity(line=5, complexity=2)
29
+ >>> print(f"Line {line_complexity.line} adds {line_complexity.complexity} to complexity")
30
+ """
31
+
32
+ line: int
33
+ """The line number (1-indexed) in the source code."""
34
+
35
+ complexity: int
36
+ """
37
+ The cognitive complexity value contributed by this line.
38
+
39
+ - 0: No complexity contribution (simple statements)
40
+ - 1+: Complexity added by control flow structures on this line
41
+ """
42
+
43
+ def __init__(self, line: int, complexity: int) -> None: ...
44
+
45
+ class FunctionComplexity:
46
+ """
47
+ Represents the cognitive complexity analysis of a Python function.
48
+
49
+ This class provides a complete complexity analysis for a single function,
50
+ including the total complexity score and line-by-line breakdown. The
51
+ cognitive complexity score helps assess how difficult a function is to
52
+ understand and maintain.
53
+
54
+ Complexity Guidelines:
55
+ - 1-10: Simple function, easy to understand
56
+ - 11-15: Moderate complexity, consider refactoring
57
+ - 16+: High complexity, should be refactored
58
+
59
+ The analysis includes nested complexity penalties, where deeply nested
60
+ control structures add additional complexity points.
61
+
62
+ Example:
63
+ >>> func = FunctionComplexity(
64
+ ... name="process_data",
65
+ ... complexity=8,
66
+ ... line_start=10,
67
+ ... line_end=25,
68
+ ... line_complexities=[LineComplexity(12, 2), LineComplexity(15, 1)]
69
+ ... )
70
+ >>> print(f"Function '{func.name}' has complexity {func.complexity}")
71
+ >>> # Find the most complex lines
72
+ >>> complex_lines = [lc for lc in func.line_complexities if lc.complexity > 0]
73
+ """
74
+
75
+ name: str
76
+ """
77
+ The name of the function as it appears in the source code.
78
+
79
+ For methods, this includes just the method name (not the class).
80
+ For nested functions, this is the inner function name.
81
+ """
82
+
83
+ complexity: int
84
+ """
85
+ The total cognitive complexity score for this function.
86
+
87
+ This is the sum of all complexity contributions from control flow
88
+ structures within the function, including nesting penalties.
89
+ Higher values indicate more complex, harder-to-understand code.
90
+ """
91
+
92
+ line_start: int
93
+ """
94
+ The starting line number of the function definition (1-indexed).
95
+
96
+ Points to the line containing 'def function_name(...):'
97
+ """
98
+
99
+ line_end: int
100
+ """
101
+ The ending line number of the function definition (1-indexed).
102
+
103
+ Points to the last line that belongs to this function's body.
104
+ """
105
+
106
+ line_complexities: List[LineComplexity]
107
+ """
108
+ Detailed complexity information for each line in the function.
109
+
110
+ This list contains LineComplexity objects for every line in the function,
111
+ allowing you to identify exactly which lines contribute to the overall
112
+ complexity score. Lines with zero complexity are included for completeness.
113
+
114
+ Example:
115
+ >>> # Find lines that add complexity
116
+ >>> complex_lines = [lc for lc in func.line_complexities if lc.complexity > 0]
117
+ >>> for line_info in complex_lines:
118
+ ... print(f"Line {line_info.line}: +{line_info.complexity}")
119
+ """
120
+
121
+ def __init__(
122
+ self,
123
+ name: str,
124
+ complexity: int,
125
+ line_start: int,
126
+ line_end: int,
127
+ line_complexities: List[LineComplexity],
128
+ ) -> None: ...
129
+
130
+ class FileComplexity:
131
+ """
132
+ Represents the cognitive complexity analysis of a Python source file.
133
+
134
+ This class aggregates complexity information for all functions found in a
135
+ Python file, providing both individual function analysis and file-level
136
+ metrics. It's useful for understanding the overall complexity of a module
137
+ and identifying which functions need attention.
138
+
139
+ File Complexity Guidelines:
140
+ - Low: Most functions have complexity < 10
141
+ - Medium: Some functions have complexity 10-15
142
+ - High: Multiple functions have complexity > 15
143
+
144
+ The file complexity is the sum of all function complexities, helping
145
+ identify files that may be doing too much or need to be split up.
146
+
147
+ Example:
148
+ >>> file_analysis = FileComplexity(
149
+ ... path="/path/to/mymodule.py",
150
+ ... file_name="mymodule.py",
151
+ ... functions=[func1, func2, func3],
152
+ ... complexity=25
153
+ ... )
154
+ >>> # Find the most complex functions
155
+ >>> complex_funcs = [f for f in file_analysis.functions if f.complexity > 10]
156
+ >>> print(f"File has {len(complex_funcs)} complex functions")
157
+ """
158
+
159
+ path: str
160
+ """
161
+ The absolute file path to the analyzed Python file.
162
+
163
+ This is the complete path as resolved by the system, useful for
164
+ identifying the exact location of the file in the filesystem.
165
+ """
166
+
167
+ file_name: str
168
+ """
169
+ The basename of the file (filename without directory path).
170
+
171
+ Example: For "/path/to/mymodule.py", this would be "mymodule.py"
172
+ """
173
+
174
+ functions: List[FunctionComplexity]
175
+ """
176
+ List of complexity analysis results for each function in the file.
177
+
178
+ This includes all top-level functions and methods found in the file.
179
+ Nested functions are analyzed as part of their containing function.
180
+ The list is ordered by appearance in the source code.
181
+
182
+ Example:
183
+ >>> # Find the most complex function
184
+ >>> most_complex = max(file_analysis.functions, key=lambda f: f.complexity)
185
+ >>> print(f"Most complex function: {most_complex.name} ({most_complex.complexity})")
186
+
187
+ >>> # Get functions that exceed threshold
188
+ >>> threshold = 15
189
+ >>> problematic = [f for f in file_analysis.functions if f.complexity > threshold]
190
+ """
191
+
192
+ complexity: int
193
+ """
194
+ The total cognitive complexity of the entire file.
195
+
196
+ This is calculated as the sum of complexity scores from all functions
197
+ in the file. It provides a high-level metric for the overall complexity
198
+ of the module and can help identify files that need refactoring.
199
+ """
200
+
201
+ def __init__(
202
+ self,
203
+ path: str,
204
+ file_name: str,
205
+ functions: List[FunctionComplexity],
206
+ complexity: int,
207
+ ) -> None: ...
208
+
209
+ class CodeComplexity:
210
+ """
211
+ Represents the cognitive complexity analysis of a Python code string.
212
+
213
+ This class is used when analyzing Python code provided as a string rather
214
+ than from a file. It's particularly useful for:
215
+ - Analyzing code snippets or templates
216
+ - Testing complexity in unit tests
217
+ - Analyzing dynamically generated code
218
+ - Integration with code editors and IDEs
219
+
220
+ The analysis works identically to file-based analysis, parsing the code
221
+ string to identify functions and calculate their complexity scores.
222
+
223
+ Example:
224
+ >>> code = '''
225
+ ... def complex_function(x, y):
226
+ ... if x > 0:
227
+ ... for i in range(y):
228
+ ... if i % 2 == 0 and i > 5:
229
+ ... return i * x
230
+ ... return 0
231
+ ... '''
232
+ >>> analysis = code_complexity(code)
233
+ >>> print(f"Code has {len(analysis.functions)} functions")
234
+ >>> print(f"Total complexity: {analysis.complexity}")
235
+ """
236
+
237
+ functions: List[FunctionComplexity]
238
+ """
239
+ List of complexity analysis results for each function found in the code.
240
+
241
+ This includes all function definitions found in the provided code string,
242
+ analyzed in the same way as file-based analysis. Functions are ordered
243
+ by their appearance in the code.
244
+
245
+ Example:
246
+ >>> for func in analysis.functions:
247
+ ... print(f"Function '{func.name}': complexity {func.complexity}")
248
+ ... if func.complexity > 10:
249
+ ... print(" -> Consider refactoring this function")
250
+ """
251
+
252
+ complexity: int
253
+ """
254
+ The total cognitive complexity of all functions in the code string.
255
+
256
+ This is the sum of complexity scores from all functions found in the
257
+ provided code. It gives an overall measure of how complex the code is.
258
+ """
259
+
260
+ def __init__(
261
+ self, functions: List[FunctionComplexity], complexity: int
262
+ ) -> None: ...
263
+
264
+ def main(
265
+ paths: List[str], quiet: bool, exclude: List[str]
266
+ ) -> Tuple[List[FileComplexity], List[str]]:
267
+ """
268
+ Analyze cognitive complexity of Python files and directories.
269
+
270
+ This is the main analysis function that processes multiple paths, which can
271
+ be individual Python files, directories containing Python files, or Git
272
+ repository URLs. It recursively analyzes all Python files found.
273
+
274
+ The function handles various input types:
275
+ - Local Python files: '/path/to/file.py'
276
+ - Local directories: '/path/to/project/' (analyzes all .py files)
277
+ - Git repositories: 'https://github.com/user/repo.git'
278
+
279
+ Args:
280
+ paths: List of file paths, directory paths, or Git repository URLs to analyze.
281
+ Each path is processed independently and all results are combined.
282
+ quiet: If True, suppresses console output during analysis. Useful for
283
+ programmatic usage where you only want the returned data.
284
+ exclude: List of file paths, directory paths.
285
+ Each path will be excluded from the analysis.
286
+
287
+ Returns:
288
+ List of FileComplexity objects, one for each Python file analyzed.
289
+ Files are ordered by discovery order during filesystem traversal.
290
+
291
+ Raises:
292
+ Various exceptions may be raised for invalid paths, permission errors,
293
+ or Git repository access issues.
294
+
295
+ Example:
296
+ >>> # Analyze a single file
297
+ >>> results = main(['/path/to/script.py'], quiet=True)
298
+ >>>
299
+ >>> # Analyze entire project directory
300
+ >>> results = main(['/path/to/project/'], quiet=False)
301
+ >>>
302
+ >>> # Find files with high complexity
303
+ >>> complex_files = [f for f in results if f.complexity > 50]
304
+ """
305
+ ...
306
+
307
+ def code_complexity(code: str) -> CodeComplexity:
308
+ """
309
+ Analyze cognitive complexity of Python code provided as a string.
310
+
311
+ This function parses and analyzes Python source code from a string,
312
+ identifying all function definitions and calculating their cognitive
313
+ complexity scores. It's ideal for analyzing code snippets, templates,
314
+ or dynamically generated code.
315
+
316
+ The analysis follows the same cognitive complexity rules as file-based
317
+ analysis, measuring control flow structures and nesting levels that
318
+ make code harder to understand.
319
+
320
+ Args:
321
+ code: A string containing valid Python source code. The code should
322
+ be properly formatted and syntactically correct. Syntax errors
323
+ will cause the analysis to fail.
324
+
325
+ Returns:
326
+ CodeComplexity object containing the analysis results, including
327
+ individual function complexity scores and total complexity.
328
+
329
+ Raises:
330
+ SyntaxError: If the provided code string contains invalid Python syntax.
331
+
332
+ Example:
333
+ >>> code_sample = '''
334
+ ... def fibonacci(n):
335
+ ... if n <= 1:
336
+ ... return n
337
+ ... elif n == 2:
338
+ ... return 1
339
+ ... else:
340
+ ... return fibonacci(n-1) + fibonacci(n-2)
341
+ ...
342
+ ... def factorial(n):
343
+ ... result = 1
344
+ ... for i in range(1, n + 1):
345
+ ... result *= i
346
+ ... return result
347
+ ... '''
348
+ >>> analysis = code_complexity(code_sample)
349
+ >>> print(f"Found {len(analysis.functions)} functions")
350
+ >>> print(f"Total complexity: {analysis.complexity}")
351
+ >>>
352
+ >>> # Analyze each function
353
+ >>> for func in analysis.functions:
354
+ ... print(f"{func.name}: complexity {func.complexity}")
355
+ """
356
+ ...
357
+
358
+ def file_complexity(file_path: str, base_path: str) -> FileComplexity:
359
+ """
360
+ Analyze cognitive complexity of a single Python source file.
361
+
362
+ This function reads and analyzes a Python file from the filesystem,
363
+ identifying all function definitions and calculating their cognitive
364
+ complexity scores. It's useful for analyzing individual files or
365
+ integrating complexity analysis into custom tools.
366
+
367
+ The function handles file reading, parsing, and complexity calculation,
368
+ providing detailed results for all functions found in the file.
369
+
370
+ Args:
371
+ file_path: Absolute path to the Python file to analyze. The file
372
+ must exist and be readable. Should point to a .py file
373
+ containing valid Python source code.
374
+ base_path: Base directory path used for calculating relative paths
375
+ in the analysis results. This is typically the project
376
+ root directory and affects the 'path' field in the
377
+ returned FileComplexity object.
378
+
379
+ Returns:
380
+ FileComplexity object containing complete analysis results for the
381
+ file, including all functions found and their complexity scores.
382
+
383
+ Raises:
384
+ FileNotFoundError: If the specified file_path does not exist.
385
+ PermissionError: If the file cannot be read due to permissions.
386
+ SyntaxError: If the Python file contains syntax errors.
387
+ UnicodeDecodeError: If the file cannot be decoded as text.
388
+
389
+ Example:
390
+ >>> # Analyze a specific file
391
+ >>> result = file_complexity(
392
+ ... file_path="/project/src/mymodule.py",
393
+ ... base_path="/project"
394
+ ... )
395
+ >>> print(f"File: {result.file_name}")
396
+ >>> print(f"Total complexity: {result.complexity}")
397
+ >>> print(f"Functions analyzed: {len(result.functions)}")
398
+ >>>
399
+ >>> # Find the most complex function
400
+ >>> if result.functions:
401
+ ... most_complex = max(result.functions, key=lambda f: f.complexity)
402
+ ... print(f"Most complex: {most_complex.name} (score: {most_complex.complexity})")
403
+ """
404
+ ...
405
+
406
+ def output_csv(
407
+ output_path: str,
408
+ files_complexities: List[FileComplexity],
409
+ sort: str,
410
+ show_details: bool,
411
+ max_complexity: int,
412
+ ) -> None:
413
+ """
414
+ Export complexity analysis results to a CSV file for reporting and analysis.
415
+
416
+ This function creates a CSV file containing complexity metrics that can be
417
+ imported into spreadsheet applications, data analysis tools, or used for
418
+ automated reporting. The CSV format makes it easy to track complexity
419
+ trends over time or compare different codebases.
420
+
421
+ The CSV includes columns for file paths, function names, complexity scores,
422
+ line numbers, and other relevant metrics. The level of detail depends on
423
+ the show_details parameter.
424
+
425
+ Args:
426
+ output_path: File system path where the CSV file will be written.
427
+ If the file exists, it will be overwritten. The directory
428
+ must exist and be writable.
429
+ files_complexities: List of FileComplexity objects from analysis
430
+ results. Each file's functions will be included
431
+ in the CSV output.
432
+ sort: Sort order for the results in the CSV file. Valid options:
433
+ - 'asc': Sort by complexity score (ascending, lowest first)
434
+ - 'desc': Sort by complexity score (descending, highest first)
435
+ - 'name': Sort alphabetically by function name
436
+ show_details: If True, includes detailed per-function information.
437
+ If False, only includes summary information.
438
+ max_complexity: Functions with complexity above this threshold will
439
+ be highlighted or filtered in the output. Use 0 to
440
+ include all functions regardless of complexity.
441
+
442
+ Raises:
443
+ PermissionError: If the output path is not writable.
444
+ FileNotFoundError: If the output directory does not exist.
445
+
446
+ Example:
447
+ >>> results = main(['/path/to/project'], quiet=True)
448
+ >>> output_csv(
449
+ ... output_path='/tmp/complexity_report.csv',
450
+ ... files_complexities=results,
451
+ ... sort='desc',
452
+ ... show_details=True,
453
+ ... max_complexity=15
454
+ ... )
455
+ >>> print("CSV report generated successfully")
456
+ """
457
+ ...
458
+
459
+ def output_json(
460
+ output_path: str,
461
+ files_complexities: List[FileComplexity],
462
+ show_details: bool,
463
+ max_complexity: int,
464
+ ) -> None:
465
+ """
466
+ Export complexity analysis results to a JSON file for programmatic consumption.
467
+
468
+ This function serializes complexity analysis results into a structured JSON
469
+ format that can be easily consumed by other tools, CI/CD pipelines, or
470
+ custom applications. The JSON format preserves the full hierarchy of
471
+ files, functions, and line-by-line complexity information.
472
+
473
+ The JSON output is machine-readable and perfect for integration with
474
+ automated quality gates, dashboards, or further data processing. It
475
+ maintains all the detailed information from the analysis.
476
+
477
+ Args:
478
+ output_path: File system path where the JSON file will be written.
479
+ If the file exists, it will be overwritten. The directory
480
+ must exist and be writable.
481
+ files_complexities: List of FileComplexity objects from analysis
482
+ results. The complete structure will be serialized
483
+ to JSON, preserving all nested information.
484
+ show_details: If True, includes detailed line-by-line complexity
485
+ information for each function. If False, only includes
486
+ summary metrics for better performance and smaller files.
487
+ max_complexity: Functions with complexity above this threshold may
488
+ be flagged or filtered in the output. Use 0 to include
489
+ all functions regardless of complexity.
490
+
491
+ Raises:
492
+ PermissionError: If the output path is not writable.
493
+ FileNotFoundError: If the output directory does not exist.
494
+ JSONEncodeError: If the data cannot be serialized to JSON.
495
+
496
+ Example:
497
+ >>> results = main(['/path/to/project'], quiet=True)
498
+ >>> output_json(
499
+ ... output_path='/tmp/complexity_report.json',
500
+ ... files_complexities=results,
501
+ ... show_details=True,
502
+ ... max_complexity=0
503
+ ... )
504
+ >>>
505
+ >>> # The JSON can be loaded by other tools
506
+ >>> import json
507
+ >>> with open('/tmp/complexity_report.json') as f:
508
+ ... data = json.load(f)
509
+ >>> print(f"Analyzed {len(data)} files")
510
+ """
511
+ ...
512
+
513
+ def create_snapshot_file(
514
+ snapshot_file_path: str,
515
+ max_complexity_allowed: int,
516
+ files_complexities: List[FileComplexity],
517
+ ) -> None: ...
518
+ def load_snapshot_file(snapshot_file_path: str) -> List[FileComplexity]: ...
complexipy/api.py ADDED
@@ -0,0 +1,78 @@
1
+ from pathlib import Path
2
+
3
+ from complexipy import _complexipy
4
+ from complexipy._complexipy import CodeComplexity, FileComplexity
5
+
6
+
7
+ def code_complexity(
8
+ code: str,
9
+ ) -> CodeComplexity:
10
+ """
11
+ Analyze cognitive complexity of Python code provided as a string.
12
+
13
+ This function parses and analyzes Python source code from a string,
14
+ identifying all function definitions and calculating their cognitive
15
+ complexity scores. Perfect for analyzing code snippets, templates,
16
+ or dynamically generated code.
17
+
18
+ Args:
19
+ code: A string containing valid Python source code. Must be
20
+ syntactically correct Python code.
21
+
22
+ Returns:
23
+ CodeComplexity object containing the analysis results, including
24
+ individual function complexity scores and total complexity.
25
+
26
+ Raises:
27
+ SyntaxError: If the provided code string contains invalid Python syntax.
28
+
29
+ Example:
30
+ >>> code = '''
31
+ ... def fibonacci(n):
32
+ ... if n <= 1:
33
+ ... return n
34
+ ... else:
35
+ ... return fibonacci(n-1) + fibonacci(n-2)
36
+ ... '''
37
+ >>> result = code_complexity(code)
38
+ >>> print(f"Total complexity: {result.complexity}")
39
+ """
40
+ return _complexipy.code_complexity(code)
41
+
42
+
43
+ def file_complexity(file_path: str) -> FileComplexity:
44
+ """
45
+ Analyze cognitive complexity of a single Python source file.
46
+
47
+ This function reads and analyzes a Python file from the filesystem,
48
+ identifying all function definitions and calculating their cognitive
49
+ complexity scores. Useful for analyzing individual files or integrating
50
+ complexity analysis into custom tools.
51
+
52
+ Args:
53
+ file_path: Path to the Python file to analyze. Can be relative or
54
+ absolute. The file must exist and be readable.
55
+
56
+ Returns:
57
+ FileComplexity object containing complete analysis results for the
58
+ file, including all functions found and their complexity scores.
59
+
60
+ Raises:
61
+ FileNotFoundError: If the specified file does not exist.
62
+ PermissionError: If the file cannot be read due to permissions.
63
+ SyntaxError: If the Python file contains syntax errors.
64
+
65
+ Example:
66
+ >>> result = file_complexity('mymodule.py')
67
+ >>> print(f"File: {result.file_name}")
68
+ >>> print(f"Total complexity: {result.complexity}")
69
+ >>> for func in result.functions:
70
+ ... if func.complexity > 10:
71
+ ... print(f"Complex function: {func.name} ({func.complexity})")
72
+ """
73
+ path = Path(file_path)
74
+ base_path = path.parent
75
+ return _complexipy.file_complexity(
76
+ path.resolve().as_posix(),
77
+ base_path.resolve().as_posix(),
78
+ )