Skip to content

Commit

Permalink
Merge pull request #45 from andrewdea/expose-library-commands
Browse files Browse the repository at this point in the history
Expose library commands
  • Loading branch information
rohaquinlop authored Oct 16, 2024
2 parents 5df86cb + 460dcb3 commit d283084
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ jobs:
pytest tests/main.py
sdist:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Build sdist
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,13 @@ docs/_build/
# VSCode
.vscode/

# Emacs
*~
\#*\#
*.dir-locals.el

# Pyenv
.python-version

# Pyright LSP
pyrightconfig.json
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "complexipy"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
authors = ["Robin Quintero <rohaquinlop301@gmail.com>"]
license = "MIT"
Expand Down
27 changes: 20 additions & 7 deletions complexipy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
from complexipy import (
rust,
)
from complexipy.rust import (
FileComplexity,
)
from complexipy.rust import FileComplexity, CodeComplexity
import os
from pathlib import (
Path,
Expand All @@ -28,7 +26,7 @@
root_dir = Path(__file__).resolve().parent.parent
app = typer.Typer(name="complexipy")
console = Console()
version = "0.4.0"
version = "0.5.0"


@app.command()
Expand Down Expand Up @@ -77,9 +75,7 @@ def main(

console.rule(f":octopus: complexipy {version}")
start_time = time.time()
files: list[FileComplexity] = rust.main(
path, is_dir, is_url, max_complexity, file_level
)
files: list[FileComplexity] = rust.main(path, is_dir, is_url, file_level)
execution_time = time.time() - start_time
output_csv_path = f"{invocation_path}/complexipy.csv"

Expand Down Expand Up @@ -109,5 +105,22 @@ def main(
raise typer.Exit(code=1)


def code_complexity(
code: str,
file_level: bool = True,
) -> CodeComplexity:
return rust.code_complexity(code, file_level)


def file_complexity(file_path: str, file_level: bool = True) -> FileComplexity:
path = Path(file_path)
base_path = path.parent
return rust.file_complexity(
file_path=path.resolve().as_posix(),
base_path=base_path.resolve().as_posix(),
_file_level=file_level,
)


if __name__ == "__main__":
app()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ classifiers = [
]
dynamic = ["version"]

dependencies = ["typer[all]"]
dependencies = ["typer==0.12.5"]

[project.scripts]
complexipy = "complexipy.main:app"
Expand Down
7 changes: 7 additions & 0 deletions src/classes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ pub struct FileComplexity {
pub functions: Vec<FunctionComplexity>,
pub complexity: u64,
}

#[derive(Clone)]
#[pyclass(module = "complexipy", get_all)]
pub struct CodeComplexity {
pub functions: Vec<FunctionComplexity>,
pub complexity: u64,
}
62 changes: 43 additions & 19 deletions src/cognitive_complexity/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
pub mod utils;

use crate::classes::{FileComplexity, FunctionComplexity};
use crate::classes::{FileComplexity, FunctionComplexity, CodeComplexity};
use ignore::Walk;
use indicatif::ProgressBar;
use indicatif::ProgressStyle;
use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;
use rayon::prelude::*;
use rustpython_parser::{
ast::{self, Stmt},
Expand All @@ -24,7 +25,6 @@ pub fn main(
path: &str,
is_dir: bool,
is_url: bool,
max_complexity: usize,
file_level: bool,
) -> PyResult<Vec<FileComplexity>> {
let mut ans: Vec<FileComplexity> = Vec::new();
Expand Down Expand Up @@ -62,21 +62,21 @@ pub fn main(

let repo_path = dir.path().join(&repo_name).to_str().unwrap().to_string();

match evaluate_dir(&repo_path, max_complexity, file_level) {
match evaluate_dir(&repo_path, file_level) {
Ok(files_complexity) => ans = files_complexity,
Err(e) => return Err(e),
}

dir.close()?;
} else if is_dir {
match evaluate_dir(path, max_complexity, file_level) {
match evaluate_dir(path, file_level) {
Ok(files_complexity) => ans = files_complexity,
Err(e) => return Err(e),
}
} else {
let parent_dir = path::Path::new(path).parent().unwrap().to_str().unwrap();

match cognitive_complexity(path, parent_dir, max_complexity, file_level) {
match file_complexity(path, parent_dir, file_level) {
Ok(file_complexity) => ans.push(file_complexity),
Err(e) => return Err(e),
}
Expand All @@ -91,7 +91,6 @@ pub fn main(

fn evaluate_dir(
path: &str,
max_complexity: usize,
file_level: bool,
) -> PyResult<Vec<FileComplexity>> {
let mut files_paths: Vec<String> = Vec::new();
Expand Down Expand Up @@ -122,7 +121,7 @@ fn evaluate_dir(
.par_iter()
.map(|file_path| {
pb.inc(1);
match cognitive_complexity(file_path, parent_dir, max_complexity, file_level) {
match file_complexity(file_path, parent_dir, file_level) {
Ok(file_complexity) => Ok(file_complexity),
Err(e) => Err(e),
}
Expand All @@ -139,22 +138,48 @@ fn evaluate_dir(

/// Calculate the cognitive complexity of a python file.
#[pyfunction]
pub fn cognitive_complexity(
pub fn file_complexity(
file_path: &str,
base_path: &str,
_max_complexity: usize,
_file_level: bool,
) -> PyResult<FileComplexity> {
let path = path::Path::new(file_path);
let file_name = path.file_name().unwrap().to_str().unwrap();
let relative_path = path.strip_prefix(base_path).unwrap().to_str().unwrap();

let code = std::fs::read_to_string(file_path)?;
let ast = ast::Suite::parse(&code, "<embedded>").unwrap();

let code_complexity = match code_complexity(&code, _file_level) {
Ok(v) => v,
Err(e) => return Err(
PyValueError::
new_err(format!("Failed to compute code_complexity; error: {}", e))),
};

Ok(FileComplexity {
path: relative_path.to_string(),
file_name: file_name.to_string(),
complexity: code_complexity.complexity,
functions: code_complexity.functions,
})
}

/// Calculate the cognitive complexity of a string of python code.
#[pyfunction]
pub fn code_complexity(
code: &str,
_file_level: bool,
) -> PyResult<CodeComplexity> {
let ast = match ast::Suite::parse(&code, "<embedded>") {
Ok(v) => v,
Err(e) => return Err(
PyValueError::
new_err(format!("Failed to parse this code; error: {}", e))),
};

let mut complexity: u64 = 0;
let path = path::Path::new(file_path);
let file_name = path.file_name().unwrap().to_str().unwrap();
let mut functions: Vec<FunctionComplexity> = Vec::new();

let relative_path = path.strip_prefix(base_path).unwrap().to_str().unwrap();

if _file_level {
for node in ast.iter() {
complexity += statement_cognitive_complexity(node.clone(), 0)?;
Expand All @@ -165,14 +190,13 @@ pub fn cognitive_complexity(
complexity = c;
}

Ok(FileComplexity {
path: relative_path.to_string(),
file_name: file_name.to_string(),
complexity: complexity,
functions: functions,
Ok(CodeComplexity {
functions,
complexity,
})
}


fn function_level_cognitive_complexity(
ast: &Vec<Stmt>,
) -> PyResult<(Vec<FunctionComplexity>, u64)> {
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
mod classes;
mod cognitive_complexity;

use classes::{FileComplexity, FunctionComplexity};
use cognitive_complexity::main;
use classes::{FileComplexity, FunctionComplexity, CodeComplexity};
use cognitive_complexity::{code_complexity, file_complexity, main};
use cognitive_complexity::utils::{output_csv_file_level, output_csv_function_level};
use pyo3::prelude::*;

/// A Python module implemented in Rust.
#[pymodule]
fn rust(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(main, m)?)?;
m.add_function(wrap_pyfunction!(file_complexity, m)?)?;
m.add_function(wrap_pyfunction!(code_complexity, m)?)?;
m.add_function(wrap_pyfunction!(output_csv_file_level, m)?)?;
m.add_function(wrap_pyfunction!(output_csv_function_level, m)?)?;
m.add_class::<FileComplexity>()?;
m.add_class::<FunctionComplexity>()?;
m.add_class::<CodeComplexity>()?;
Ok(())
}
Loading

0 comments on commit d283084

Please sign in to comment.