Skip to content

Commit

Permalink
feat(build|docs): #15 add function level complexity analysis
Browse files Browse the repository at this point in the history
- Add function level complexity analysis
- Add tests for function level complexity analysis
- Enhance the output of the analysis
- Create a new file to store the types used in the project
- Create a new file to store the utils used in the project
- Write the csv file with the function level complexity analysis
- Write the csv files using Rust instead of Python.
- Enhance the table output of the analysis
- Update the README file to include the new features
  • Loading branch information
rohaquinlop committed Mar 6, 2024
1 parent a175c28 commit d26d0d5
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 86 deletions.
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ name = "complexipy"
crate-type = ["cdylib"]

[dependencies]
csv = "1.3.0"
env_logger = "0.11.1"
ignore = "0.4.22"
log = "0.4.20"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ $ complexipy https://github.com/rohaquinlop/complexipy -o

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
This project is licensed under the MIT License - see the [LICENSE](https://github.com/rohaquinlop/complexipy/blob/main/LICENSE) file
for details.

## Acknowledgments
Expand Down
102 changes: 55 additions & 47 deletions complexipy/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
from pathlib import Path
from complexipy import rust
import csv
from enum import Enum
from .types import (
DetailTypes,
Level,
)
from .utils import (
create_table_file_level,
create_table_function_level,
)
from complexipy import (
rust,
)
from complexipy.rust import (
FileComplexity,
)
import os
from pathlib import (
Path,
)
import re
from rich.console import Console
from rich.table import Table
from rich.align import (
Align,
)
from rich.console import (
Console,
)
import time
import typer

Expand All @@ -15,11 +32,6 @@
version = "0.3.0"


class DetailTypes(Enum):
low = "low" # Show only files with complexity above the max_complexity
normal = "normal" # Show all files with their complexity


@app.command()
def main(
path: str = typer.Argument(
Expand All @@ -29,7 +41,7 @@ def main(
15,
"--max-complexity",
"-c",
help="The maximum complexity allowed per file, set this value as 0 to set it as unlimited.",
help="The maximum complexity allowed per file, set this value as 0 to set it as unlimited. Default is 15.",
),
output: bool = typer.Option(
False, "--output", "-o", help="Output the results to a CSV file."
Expand All @@ -38,58 +50,54 @@ def main(
DetailTypes.normal.value,
"--details",
"-d",
help="Specify how detailed should be output.",
help="Specify how detailed should be output, it can be 'low' or 'normal'. Default is 'normal'.",
),
level: Level = typer.Option(
Level.function.value,
"--level",
"-l",
help="Specify the level of measurement, it can be 'function' or 'file'. Default is 'function'.",
),
):
has_success = True
is_dir = Path(path).is_dir()
_url_pattern = (
r"^(https:\/\/|http:\/\/|www\.|git@)(github|gitlab)\.com(\/[\w.-]+){2,}$"
)
is_url = bool(re.match(_url_pattern, path))
invocation_path = os.getcwd()
file_level = level == Level.file

console.rule(f"complexipy {version} :octopus:")
with console.status("Analyzing the complexity of the code...", spinner="dots"):
start_time = time.time()
files = rust.main(path, is_dir, is_url, max_complexity)
files: list[FileComplexity] = rust.main(
path, is_dir, is_url, max_complexity, file_level
)
execution_time = time.time() - start_time
console.rule(":tada: Analysis completed!:tada:")
output_csv_path = f"{invocation_path}/complexipy.csv"

if output:
with open(f"{invocation_path}/complexipy.csv", "w", newline="") as file:
writer = csv.writer(file)
writer.writerow(["Path", "File Name", "Cognitive Complexity"])
for file in files:
writer.writerow([file.path, file.file_name, file.complexity])
console.print(f"Results saved to {invocation_path}/complexipy.csv")
if output and file_level:
rust.output_csv_file_level(output_csv_path, files)
console.print(f"Results saved in {output_csv_path}")
if output and not file_level:
rust.output_csv_function_level(output_csv_path, files)
console.print(f"Results saved in {output_csv_path}")

# Summary
table = Table(
title="Summary", show_header=True, header_style="bold magenta", show_lines=True
)
table.add_column("Path")
table.add_column("File")
table.add_column("Complexity")
total_complexity = 0
for file in files:
total_complexity += file.complexity
if file.complexity > max_complexity and max_complexity != 0:
table.add_row(
f"{file.path}",
f"[green]{file.file_name}[/green]",
f"[red]{file.complexity}[/red]",
)
has_success = False
elif details != DetailTypes.low or max_complexity == 0:
table.add_row(
f"{file.path}",
f"[green]{file.file_name}[/green]",
f"[blue]{file.complexity}[/blue]",
)
console.print(table)
if file_level:
table, has_success, total_complexity = create_table_file_level(
files, max_complexity, details
)
else:
table, has_success, total_complexity = create_table_function_level(
files, max_complexity, details
)
console.print(Align.center(table))
console.print(f":brain: Total Cognitive Complexity in {path}: {total_complexity}")
console.print(f"{len(files)} files analyzed in {execution_time:.4f} seconds")
console.print(
f"{len(files)} file{'s' if len(files)> 1 else ''} analyzed in {execution_time:.4f} seconds"
)
console.rule(":tada: Analysis completed! :tada:")

if not has_success:
raise typer.Exit(code=1)
Expand Down
11 changes: 11 additions & 0 deletions complexipy/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from enum import Enum


class DetailTypes(Enum):
low = "low" # Show only files with complexity above the max_complexity
normal = "normal" # Show all files with their complexity


class Level(Enum):
function = "function"
file = "file"
72 changes: 72 additions & 0 deletions complexipy/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from .types import (
DetailTypes,
)
from complexipy.rust import (
FileComplexity,
)
from rich.table import Table


def create_table_file_level(
files: list[FileComplexity], max_complexity: int, details: DetailTypes
) -> tuple[Table, bool, int]:
has_success = True

table = Table(
title="Summary", show_header=True, header_style="bold magenta", show_lines=True
)
table.add_column("Path")
table.add_column("File")
table.add_column("Complexity")
total_complexity = 0
for file in files:
total_complexity += file.complexity
if file.complexity > max_complexity and max_complexity != 0:
table.add_row(
f"{file.path}",
f"[green]{file.file_name}[/green]",
f"[red]{file.complexity}[/red]",
)
has_success = False
elif details != DetailTypes.low or max_complexity == 0:
table.add_row(
f"{file.path}",
f"[green]{file.file_name}[/green]",
f"[blue]{file.complexity}[/blue]",
)
return table, has_success, total_complexity


def create_table_function_level(
files: list[FileComplexity], complexity: int, details: DetailTypes
) -> tuple[Table, bool, int]:
has_success = True

table = Table(
title="Summary", show_header=True, header_style="bold magenta", show_lines=True
)
table.add_column("Path")
table.add_column("File")
table.add_column("Function")
table.add_column("Complexity")
total_complexity = 0
for file in files:
total_complexity += file.complexity
for function in file.functions:
total_complexity += function.complexity
if function.complexity > complexity and complexity != 0:
table.add_row(
f"{file.path}",
f"[green]{file.file_name}[/green]",
f"[green]{function.name}[/green]",
f"[red]{function.complexity}[/red]",
)
has_success = False
elif details != DetailTypes.low or complexity == 0:
table.add_row(
f"{file.path}",
f"[green]{file.file_name}[/green]",
f"[green]{function.name}[/green]",
f"[blue]{function.complexity}[/blue]",
)
return table, has_success, total_complexity
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ $ complexipy https://github.com/rohaquinlop/complexipy -o

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
This project is licensed under the MIT License - see the [LICENSE](https://github.com/rohaquinlop/complexipy/blob/main/LICENSE) file
for details.

## Acknowledgments
Expand Down
3 changes: 2 additions & 1 deletion src/classes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use pyo3::prelude::*;

#[derive(Clone)]
#[pyclass(module = "complexipy", get_all)]
pub struct FunctionComplexity {
pub name: String,
Expand All @@ -11,6 +12,6 @@ pub struct FunctionComplexity {
pub struct FileComplexity {
pub path: String,
pub file_name: String,
// pub functions: Vec<FunctionComplexity>,
pub functions: Vec<FunctionComplexity>,
pub complexity: u64,
}
Loading

0 comments on commit d26d0d5

Please sign in to comment.