complexipy 0.3.1__cp39-none-win32.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 +0 -0
- complexipy/main.py +113 -0
- complexipy/rust.cp39-win32.pyd +0 -0
- complexipy/types.py +17 -0
- complexipy/utils.py +139 -0
- complexipy-0.3.1.dist-info/METADATA +244 -0
- complexipy-0.3.1.dist-info/RECORD +10 -0
- complexipy-0.3.1.dist-info/WHEEL +4 -0
- complexipy-0.3.1.dist-info/entry_points.txt +2 -0
- complexipy-0.3.1.dist-info/license_files/LICENSE +21 -0
complexipy/__init__.py
ADDED
|
File without changes
|
complexipy/main.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from .types import (
|
|
2
|
+
DetailTypes,
|
|
3
|
+
Level,
|
|
4
|
+
Sort,
|
|
5
|
+
)
|
|
6
|
+
from .utils import (
|
|
7
|
+
output_summary,
|
|
8
|
+
has_success_file_level,
|
|
9
|
+
has_success_function_level,
|
|
10
|
+
)
|
|
11
|
+
from complexipy import (
|
|
12
|
+
rust,
|
|
13
|
+
)
|
|
14
|
+
from complexipy.rust import (
|
|
15
|
+
FileComplexity,
|
|
16
|
+
)
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import (
|
|
19
|
+
Path,
|
|
20
|
+
)
|
|
21
|
+
import re
|
|
22
|
+
from rich.console import (
|
|
23
|
+
Console,
|
|
24
|
+
)
|
|
25
|
+
import time
|
|
26
|
+
import typer
|
|
27
|
+
|
|
28
|
+
root_dir = Path(__file__).resolve().parent.parent
|
|
29
|
+
app = typer.Typer(name="complexipy")
|
|
30
|
+
console = Console()
|
|
31
|
+
version = "0.3.1"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.command()
|
|
35
|
+
def main(
|
|
36
|
+
path: str = typer.Argument(
|
|
37
|
+
help="Path to the directory or file to analyze, it can be a local path or a git repository URL.",
|
|
38
|
+
),
|
|
39
|
+
max_complexity: int = typer.Option(
|
|
40
|
+
15,
|
|
41
|
+
"--max-complexity",
|
|
42
|
+
"-c",
|
|
43
|
+
help="The maximum complexity allowed per file, set this value as 0 to set it as unlimited. Default is 15.",
|
|
44
|
+
),
|
|
45
|
+
output: bool = typer.Option(
|
|
46
|
+
False, "--output", "-o", help="Output the results to a CSV file."
|
|
47
|
+
),
|
|
48
|
+
details: DetailTypes = typer.Option(
|
|
49
|
+
DetailTypes.normal.value,
|
|
50
|
+
"--details",
|
|
51
|
+
"-d",
|
|
52
|
+
help="Specify how detailed should be output, it can be 'low' or 'normal'. Default is 'normal'.",
|
|
53
|
+
),
|
|
54
|
+
level: Level = typer.Option(
|
|
55
|
+
Level.function.value,
|
|
56
|
+
"--level",
|
|
57
|
+
"-l",
|
|
58
|
+
help="Specify the level of measurement, it can be 'function' or 'file'. Default is 'function'.",
|
|
59
|
+
),
|
|
60
|
+
quiet: bool = typer.Option(
|
|
61
|
+
False, "--quiet", "-q", help="Suppress the output to the console."
|
|
62
|
+
),
|
|
63
|
+
sort: Sort = typer.Option(
|
|
64
|
+
Sort.asc.value,
|
|
65
|
+
"--sort",
|
|
66
|
+
"-s",
|
|
67
|
+
help="Sort the output by complexity, it can be 'asc', 'desc' or 'name'. Default is 'asc'.",
|
|
68
|
+
),
|
|
69
|
+
):
|
|
70
|
+
is_dir = Path(path).is_dir()
|
|
71
|
+
_url_pattern = (
|
|
72
|
+
r"^(https:\/\/|http:\/\/|www\.|git@)(github|gitlab)\.com(\/[\w.-]+){2,}$"
|
|
73
|
+
)
|
|
74
|
+
is_url = bool(re.match(_url_pattern, path))
|
|
75
|
+
invocation_path = os.getcwd()
|
|
76
|
+
file_level = level == Level.file
|
|
77
|
+
|
|
78
|
+
console.rule(f":octopus: complexipy {version}")
|
|
79
|
+
start_time = time.time()
|
|
80
|
+
files: list[FileComplexity] = rust.main(
|
|
81
|
+
path, is_dir, is_url, max_complexity, file_level
|
|
82
|
+
)
|
|
83
|
+
execution_time = time.time() - start_time
|
|
84
|
+
output_csv_path = f"{invocation_path}/complexipy.csv"
|
|
85
|
+
|
|
86
|
+
if output and file_level:
|
|
87
|
+
rust.output_csv_file_level(output_csv_path, files, sort.value)
|
|
88
|
+
console.print(f"Results saved in {output_csv_path}")
|
|
89
|
+
if output and not file_level:
|
|
90
|
+
rust.output_csv_function_level(output_csv_path, files, sort.value)
|
|
91
|
+
console.print(f"Results saved in {output_csv_path}")
|
|
92
|
+
|
|
93
|
+
# Summary
|
|
94
|
+
if not quiet:
|
|
95
|
+
has_success = output_summary(
|
|
96
|
+
console, file_level, files, max_complexity, details, path, sort
|
|
97
|
+
)
|
|
98
|
+
if quiet and not file_level:
|
|
99
|
+
has_success = has_success_function_level(files, max_complexity)
|
|
100
|
+
if quiet and file_level:
|
|
101
|
+
has_success = has_success_file_level(files, max_complexity)
|
|
102
|
+
|
|
103
|
+
console.print(
|
|
104
|
+
f"{len(files)} file{'s' if len(files)> 1 else ''} analyzed in {execution_time:.4f} seconds"
|
|
105
|
+
)
|
|
106
|
+
console.rule(":tada: Analysis completed! :tada:")
|
|
107
|
+
|
|
108
|
+
if not has_success:
|
|
109
|
+
raise typer.Exit(code=1)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
app()
|
|
Binary file
|
complexipy/types.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DetailTypes(Enum):
|
|
5
|
+
low = "low" # Show only files with complexity above the max_complexity
|
|
6
|
+
normal = "normal" # Show all files with their complexity
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Level(Enum):
|
|
10
|
+
function = "function"
|
|
11
|
+
file = "file"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Sort(Enum):
|
|
15
|
+
asc = "asc"
|
|
16
|
+
desc = "desc"
|
|
17
|
+
name = "name"
|
complexipy/utils.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from .types import (
|
|
2
|
+
DetailTypes,
|
|
3
|
+
Sort,
|
|
4
|
+
)
|
|
5
|
+
from complexipy.rust import (
|
|
6
|
+
FileComplexity,
|
|
7
|
+
FunctionComplexity,
|
|
8
|
+
)
|
|
9
|
+
from rich.align import (
|
|
10
|
+
Align,
|
|
11
|
+
)
|
|
12
|
+
from rich.console import (
|
|
13
|
+
Console,
|
|
14
|
+
)
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def output_summary(
|
|
19
|
+
console: Console,
|
|
20
|
+
file_level: bool,
|
|
21
|
+
files: list[FileComplexity],
|
|
22
|
+
max_complexity: int,
|
|
23
|
+
details: DetailTypes,
|
|
24
|
+
path: str,
|
|
25
|
+
sort: Sort,
|
|
26
|
+
) -> bool:
|
|
27
|
+
if file_level:
|
|
28
|
+
table, has_success, total_complexity = create_table_file_level(
|
|
29
|
+
files, max_complexity, details, sort
|
|
30
|
+
)
|
|
31
|
+
else:
|
|
32
|
+
table, has_success, total_complexity = create_table_function_level(
|
|
33
|
+
files, max_complexity, details, sort
|
|
34
|
+
)
|
|
35
|
+
console.print(Align.center(table))
|
|
36
|
+
console.print(f":brain: Total Cognitive Complexity in {path}: {total_complexity}")
|
|
37
|
+
|
|
38
|
+
return has_success
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_table_file_level(
|
|
42
|
+
files: list[FileComplexity], max_complexity: int, details: DetailTypes, sort: Sort
|
|
43
|
+
) -> tuple[Table, bool, int]:
|
|
44
|
+
has_success = True
|
|
45
|
+
|
|
46
|
+
table = Table(
|
|
47
|
+
title="Summary", show_header=True, header_style="bold magenta", show_lines=True
|
|
48
|
+
)
|
|
49
|
+
table.add_column("Path")
|
|
50
|
+
table.add_column("File")
|
|
51
|
+
table.add_column("Complexity")
|
|
52
|
+
total_complexity = 0
|
|
53
|
+
|
|
54
|
+
if sort != Sort.name:
|
|
55
|
+
files.sort(key=lambda x: x.complexity)
|
|
56
|
+
|
|
57
|
+
if sort == Sort.desc:
|
|
58
|
+
files.reverse()
|
|
59
|
+
|
|
60
|
+
for file in files:
|
|
61
|
+
total_complexity += file.complexity
|
|
62
|
+
if file.complexity > max_complexity and max_complexity != 0:
|
|
63
|
+
table.add_row(
|
|
64
|
+
f"{file.path}",
|
|
65
|
+
f"[green]{file.file_name}[/green]",
|
|
66
|
+
f"[red]{file.complexity}[/red]",
|
|
67
|
+
)
|
|
68
|
+
has_success = False
|
|
69
|
+
elif details != DetailTypes.low or max_complexity == 0:
|
|
70
|
+
table.add_row(
|
|
71
|
+
f"{file.path}",
|
|
72
|
+
f"[green]{file.file_name}[/green]",
|
|
73
|
+
f"[blue]{file.complexity}[/blue]",
|
|
74
|
+
)
|
|
75
|
+
return table, has_success, total_complexity
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def create_table_function_level(
|
|
79
|
+
files: list[FileComplexity],
|
|
80
|
+
complexity: int,
|
|
81
|
+
details: DetailTypes,
|
|
82
|
+
sort: bool = False,
|
|
83
|
+
) -> tuple[Table, bool, int]:
|
|
84
|
+
has_success = True
|
|
85
|
+
all_functions: list[tuple[str, str, FunctionComplexity]] = []
|
|
86
|
+
total_complexity = 0
|
|
87
|
+
|
|
88
|
+
table = Table(
|
|
89
|
+
title="Summary", show_header=True, header_style="bold magenta", show_lines=True
|
|
90
|
+
)
|
|
91
|
+
table.add_column("Path")
|
|
92
|
+
table.add_column("File")
|
|
93
|
+
table.add_column("Function")
|
|
94
|
+
table.add_column("Complexity")
|
|
95
|
+
|
|
96
|
+
for file in files:
|
|
97
|
+
total_complexity += file.complexity
|
|
98
|
+
for function in file.functions:
|
|
99
|
+
total_complexity += function.complexity
|
|
100
|
+
all_functions.append((file.path, file.file_name, function))
|
|
101
|
+
|
|
102
|
+
if sort != Sort.name:
|
|
103
|
+
all_functions.sort(key=lambda x: x[2].complexity)
|
|
104
|
+
|
|
105
|
+
if sort == Sort.desc:
|
|
106
|
+
all_functions.reverse()
|
|
107
|
+
|
|
108
|
+
for function in all_functions:
|
|
109
|
+
if function[2].complexity > complexity and complexity != 0:
|
|
110
|
+
table.add_row(
|
|
111
|
+
f"{function[0]}",
|
|
112
|
+
f"[green]{function[1]}[/green]",
|
|
113
|
+
f"[green]{function[2].name}[/green]",
|
|
114
|
+
f"[red]{function[2].complexity}[/red]",
|
|
115
|
+
)
|
|
116
|
+
has_success = False
|
|
117
|
+
elif details != DetailTypes.low or complexity == 0:
|
|
118
|
+
table.add_row(
|
|
119
|
+
f"{function[0]}",
|
|
120
|
+
f"[green]{function[1]}[/green]",
|
|
121
|
+
f"[green]{function[2].name}[/green]",
|
|
122
|
+
f"[blue]{function[2].complexity}[/blue]",
|
|
123
|
+
)
|
|
124
|
+
return table, has_success, total_complexity
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def has_success_file_level(files: list[FileComplexity], max_complexity: int) -> bool:
|
|
128
|
+
for file in files:
|
|
129
|
+
if file.complexity > max_complexity and max_complexity != 0:
|
|
130
|
+
return False
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def has_success_function_level(files: list[FileComplexity], complexity: int) -> bool:
|
|
135
|
+
for file in files:
|
|
136
|
+
for function in file.functions:
|
|
137
|
+
if function.complexity > complexity and complexity != 0:
|
|
138
|
+
return False
|
|
139
|
+
return True
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: complexipy
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Dist: typer[all]
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Summary: An extremely fast Python library to calculate the cognitive complexity of Python files, written in Rust.
|
|
10
|
+
Keywords: cognitive,complexity,cognitive complexity,rust,fast
|
|
11
|
+
Home-Page: https://github.com/rohaquinlop/complexipy
|
|
12
|
+
Author: Robin Quintero <rohaquinlop301@gmail.com>
|
|
13
|
+
Author-email: Robin Quintero <rohaquinlop301@gmail.com>
|
|
14
|
+
License: MIT
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
17
|
+
Project-URL: Source Code, https://github.com/rohaquinlop/complexipy
|
|
18
|
+
|
|
19
|
+
# complexipy
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://rohaquinlop.github.io/complexipy/"><img src="https://raw.githubusercontent.com/rohaquinlop/complexipy/main/docs/img/logo-vector.svg" alt="complexipy"></a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
<em>An extremely fast Python library to calculate the cognitive complexity of Python files, written in Rust.</em>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<a href="https://sonarcloud.io/summary/new_code?id=rohaquinlop_complexipy" target="_blank">
|
|
31
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=rohaquinlop_complexipy&metric=alert_status" alt="Quality Gate">
|
|
32
|
+
</a>
|
|
33
|
+
<a href="https://pypi.org/project/complexipy" target="_blank">
|
|
34
|
+
<img src="https://img.shields.io/pypi/v/complexipy?color=%2334D058&label=pypi%20package" alt="Package version">
|
|
35
|
+
</a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
Cognitive Complexity breaks from using mathematical models to assess software
|
|
40
|
+
maintainability by combining Cyclomatic Complexity precedents with human
|
|
41
|
+
assessment. It yields method complexity scores that align well with how
|
|
42
|
+
developers perceive maintainability. Read the white paper here: [Cognitive Complexity, a new way of measuring understandability](https://www.sonarsource.com/resources/cognitive-complexity/)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
**Documentation**: <a href="https://rohaquinlop.github.io/complexipy/" target="_blank">https://rohaquinlop.github.io/complexipy/</a>
|
|
48
|
+
|
|
49
|
+
**Source Code**: <a href="https://github.com/rohaquinlop/complexipy" target="_blank">https://github.com/rohaquinlop/complexipy</a>
|
|
50
|
+
|
|
51
|
+
**PyPI**: <a href="https://pypi.org/project/complexipy/" target="_blank">https://pypi.org/project/complexipy/</a>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Contributors
|
|
57
|
+
|
|
58
|
+
<p align="center">
|
|
59
|
+
<a href = "https://github.com/rohaquinlop/complexipy/graphs/contributors">
|
|
60
|
+
<img src = "https://contrib.rocks/image?repo=rohaquinlop/complexipy"/>
|
|
61
|
+
</a>
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
Made with [contributors-img](https://contrib.rocks)
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- Python >= 3.8
|
|
69
|
+
- You also need to install `git` in your computer if you want to analyze a git repository.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install complexipy
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
To run **complexipy** you can use the following command:
|
|
80
|
+
|
|
81
|
+
```shell
|
|
82
|
+
complexipy . # Use complexipy to analyze the current directory and any subdirectories
|
|
83
|
+
complexipy path/to/directory # Use complexipy to analyze a specific directory and any subdirectories
|
|
84
|
+
complexipy git_repository_url # Use complexipy to analyze a git repository
|
|
85
|
+
complexipy path/to/file.py # Use complexipy to analyze a specific file
|
|
86
|
+
complexipy path/to/file.py -c 20 # Use the -c option to set the maximum congnitive complexity, default is 15
|
|
87
|
+
complexipy path/to/directory -c 0 # Set the maximum cognitive complexity to 0 to disable the exit with error
|
|
88
|
+
complexipy path/to/directory -o # Use the -o option to output the results to a CSV file, default is False
|
|
89
|
+
complexipy path/to/directory -d low # Use the -d option to set detail level, default is "normal". If set to "low" will show only files with complexity greater than the maximum complexity
|
|
90
|
+
complexipy path/to/directory -l file # Use the -l option to set the level of measurement, default is "function". If set to "file" will measure the complexity of the file and will validate the maximum complexity according to the file complexity.
|
|
91
|
+
complexipy path/to/directory -q # Use the -q option to disable the output to the console, default is False.
|
|
92
|
+
complexipy path/to/directory -s desc # Use the -s option to set the sort order, default is "asc". If set to "desc" will sort the results in descending order. If set to "asc" will sort the results in ascending order. If set to "name" will sort the results by name.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Options
|
|
96
|
+
|
|
97
|
+
- `-c` or `--max-complexity`: Set the maximum cognitive complexity, default is 15.
|
|
98
|
+
If the cognitive complexity of a file is greater than the maximum cognitive,
|
|
99
|
+
then the return code will be 1 and exit with error, otherwise it will be 0.
|
|
100
|
+
If set to 0, the exit with error will be disabled.
|
|
101
|
+
- `-o` or `--output`: Output the results to a CSV file, default is False. The
|
|
102
|
+
filename will be `complexipy.csv` and will be saved in the invocation directory.
|
|
103
|
+
- `-d` or `--detail`: Set the detail level, default is "normal". If set to "low"
|
|
104
|
+
will show only files or functions with complexity greater than the maximum
|
|
105
|
+
complexity.
|
|
106
|
+
- `-l` or `--level` Set the level of measurement, default is "function". If set
|
|
107
|
+
to "file" will measure the complexity of the file and will validate the maximum
|
|
108
|
+
complexity according to the file complexity. If set to "function" will measure
|
|
109
|
+
the complexity of the functions and will validate the maximum complexity
|
|
110
|
+
according to the function complexity. This option is useful if you want to set
|
|
111
|
+
a maximum complexity according for each file or for each function in the file
|
|
112
|
+
(or files).
|
|
113
|
+
- `-q` or `--quiet`: Disable the output to the console, default is False.
|
|
114
|
+
- `-s` or `--sort`: Set the sort order, default is "asc". If set to "desc" will
|
|
115
|
+
sort the results in descending order. If set to "asc" will sort the results in
|
|
116
|
+
ascending order. If set to "name" will sort the results by name. This option will
|
|
117
|
+
affect the output to the console and the output to the CSV file.
|
|
118
|
+
|
|
119
|
+
If the cognitive complexity of a file or a function is greater than the maximum
|
|
120
|
+
cognitive cognitive complexity, then the return code will be 1 and exit with
|
|
121
|
+
error, otherwise it will be 0.
|
|
122
|
+
|
|
123
|
+
## Example
|
|
124
|
+
|
|
125
|
+
### Analyzing a file
|
|
126
|
+
|
|
127
|
+
For example, given the following file:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
def a_decorator(a, b):
|
|
131
|
+
def inner(func):
|
|
132
|
+
return func
|
|
133
|
+
return inner
|
|
134
|
+
|
|
135
|
+
def b_decorator(a, b):
|
|
136
|
+
def inner(func):
|
|
137
|
+
if func:
|
|
138
|
+
return None
|
|
139
|
+
return func
|
|
140
|
+
return inner
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The cognitive complexity of the file is 1, and the output of the command
|
|
144
|
+
`complexipy path/to/file.py` will be:
|
|
145
|
+
|
|
146
|
+
```txt
|
|
147
|
+
───────────────────────────── 🐙 complexipy 0.3.1 ──────────────────────────────
|
|
148
|
+
Summary
|
|
149
|
+
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
|
|
150
|
+
┃ Path ┃ File ┃ Function ┃ Complexity ┃
|
|
151
|
+
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
|
|
152
|
+
│ test_decorator.py │ test_decorator.py │ a_decorator │ 0 │
|
|
153
|
+
├───────────────────┼───────────────────┼─────────────┼────────────┤
|
|
154
|
+
│ test_decorator.py │ test_decorator.py │ b_decorator │ 1 │
|
|
155
|
+
└───────────────────┴───────────────────┴─────────────┴────────────┘
|
|
156
|
+
🧠 Total Cognitive Complexity in ./tests/src/test_decorator.py: 1
|
|
157
|
+
1 file analyzed in 0.0032 seconds
|
|
158
|
+
────────────────────────── 🎉 Analysis completed! 🎉 ───────────────────────────
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Explaining the results of the analysis
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
def a_decorator(a, b): # 0
|
|
165
|
+
def inner(func): # 0
|
|
166
|
+
return func # 0
|
|
167
|
+
return inner # 0
|
|
168
|
+
|
|
169
|
+
def b_decorator(a, b): # 0
|
|
170
|
+
def inner(func): # 0
|
|
171
|
+
if func: # 1 (nested = 0), total 1
|
|
172
|
+
return None # 0
|
|
173
|
+
return func # 0
|
|
174
|
+
return inner # 0
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The cognitive complexity of the file is 1, and the cognitive complexity of the
|
|
178
|
+
function `b_decorator` is 1. This example is simple, but it shows how
|
|
179
|
+
**complexipy** calculates the cognitive complexity according to the specifications
|
|
180
|
+
of the paper "Cognitive Complexity a new way to measure understandability",
|
|
181
|
+
considering the decorators and the if statement.
|
|
182
|
+
|
|
183
|
+
#### Output to a CSV file
|
|
184
|
+
|
|
185
|
+
If you want to output the results to a CSV file, you can use the `-o` option,
|
|
186
|
+
this is really useful if you want to integrate **complexipy** with other tools,
|
|
187
|
+
for example, a CI/CD pipeline. You will get the output in the console and will
|
|
188
|
+
create a CSV file with the results of the analysis.
|
|
189
|
+
|
|
190
|
+
The filename will be `complexipy.csv` and will be saved in the current directory.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
$ complexipy path/to/file.py -o
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The output will be:
|
|
197
|
+
|
|
198
|
+
```csv
|
|
199
|
+
Path,File Name,Function Name,Cognitive Complexity
|
|
200
|
+
test_decorator.py,test_decorator.py,a_decorator,0
|
|
201
|
+
test_decorator.py,test_decorator.py,b_decorator,1
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Analyzing a directory
|
|
205
|
+
|
|
206
|
+
You can also analyze a directory, for example:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
$ complexipy .
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
And **complexipy** will analyze all the files in the current directory and any
|
|
213
|
+
subdirectories.
|
|
214
|
+
|
|
215
|
+
### Analyzing a git repository
|
|
216
|
+
|
|
217
|
+
You can also analyze a git repository, for example:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
$ complexipy https://github.com/rohaquinlop/complexipy
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
And to generate the output to a CSV file:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
$ complexipy https://github.com/rohaquinlop/complexipy -o
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/rohaquinlop/complexipy/blob/main/LICENSE) file
|
|
232
|
+
for details.
|
|
233
|
+
|
|
234
|
+
## Acknowledgments
|
|
235
|
+
|
|
236
|
+
- Thanks to G. Ann Campbell for publishing the paper "Cognitive Complexity a new
|
|
237
|
+
way to measure understandability".
|
|
238
|
+
- This project is inspired by the Sonar way to calculate the cognitive
|
|
239
|
+
complexity.
|
|
240
|
+
|
|
241
|
+
## References
|
|
242
|
+
|
|
243
|
+
- [Cognitive Complexity](https://www.sonarsource.com/resources/cognitive-complexity/)
|
|
244
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
complexipy-0.3.1.dist-info/METADATA,sha256=rZ67V2LDk3PZwrqwRWIDYZiZ_QSQ94IhcHI4Yawmge8,10740
|
|
2
|
+
complexipy-0.3.1.dist-info/WHEEL,sha256=w5VA4sSkZJ067j6Hofiv4SJGeMhsWx20dsMlNwmPyXI,90
|
|
3
|
+
complexipy-0.3.1.dist-info/entry_points.txt,sha256=7BRIkSuvn7A9AIDAQhKigIXLQxrXjnz7bsB1wYis61g,49
|
|
4
|
+
complexipy-0.3.1.dist-info/license_files/LICENSE,sha256=b9V0Fd9UX8GbtBEyaglrPjW1ihY1Vfl1QWOYzdCd1MA,1092
|
|
5
|
+
complexipy/main.py,sha256=XNwRjm0tgTdrhR8Ku20uxToq2UqkftC-CeqlxvwjEcY,3381
|
|
6
|
+
complexipy/types.py,sha256=6jjU1jEIMj5_2jfceINKgfPYPLDA8zGQo8bR17jjVYE,341
|
|
7
|
+
complexipy/utils.py,sha256=YLfKQd43Zy6pMe-y0FBuTv5ePxMSnv3lLYJKG7XEKac,4285
|
|
8
|
+
complexipy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
complexipy/rust.cp39-win32.pyd,sha256=vy-7AUTkpptm9AZj1EWS7cf8VV9tEOlgZh-ROMOPxco,4726272
|
|
10
|
+
complexipy-0.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Robin Quintero
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|