From 79990b93d3b56116725b7718d2eabdf43c813f1c Mon Sep 17 00:00:00 2001 From: Reiner Gerecke Date: Wed, 28 Dec 2022 09:37:38 +0100 Subject: [PATCH] Accept options in check --- Cargo.lock | 11 ++++++++ Cargo.toml | 1 + ruff/__init__.pyi | 2 +- ruff/tests.py | 24 +++++++++++++++++ src/lib_native.rs | 4 ++- src/lib_python.rs | 69 +++++++++++++++++++++++++++-------------------- 6 files changed, 80 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79947a11cff6f..0e9e05349e732 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1734,6 +1734,16 @@ dependencies = [ "syn", ] +[[package]] +name = "pythonize" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7f0c136f5fbc01868185eef462800e49659eb23acca83b9e884367a006acb6" +dependencies = [ + "pyo3", + "serde", +] + [[package]] name = "quick-junit" version = "0.3.2" @@ -1992,6 +2002,7 @@ dependencies = [ "once_cell", "path-absolutize", "pyo3", + "pythonize", "quick-junit", "rayon", "regex", diff --git a/Cargo.toml b/Cargo.toml index b6acdc84856f0..d9ea9350d40b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ clearscreen = { version = "1.0.10" } rayon = { version = "1.5.3" } update-informer = { version = "0.5.0", default-features = false, features = ["pypi"], optional = true } pyo3 = { version = "0.17.3", features = ["extension-module", "abi3", "abi3-py37", "anyhow"] } +pythonize = "0.17.0" # https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support # For (future) wasm-pack support diff --git a/ruff/__init__.pyi b/ruff/__init__.pyi index 71887144707c6..b39cc17890e0b 100644 --- a/ruff/__init__.pyi +++ b/ruff/__init__.pyi @@ -9,4 +9,4 @@ class Message: location: Location end_location: Location -def check(contents: str | None, path: str | None) -> list(Message): ... \ No newline at end of file +def check(contents: str | None, path: str | None, options: Mapping[str, Any] | None) -> list(Message): ... \ No newline at end of file diff --git a/ruff/tests.py b/ruff/tests.py index e5372863ac8e8..d12317a2b0e49 100644 --- a/ruff/tests.py +++ b/ruff/tests.py @@ -21,6 +21,30 @@ def test_something(self): self.assertEqual(results[0].end_location.row, 3) self.assertEqual(results[0].end_location.column, 10) + def test_options(self): + results = check( + """ +import sys + +print("This is a really long text") + """, + options={"line-length": 10, "ignore": ["F401"]}, + ) + self.assertEqual(len(results), 1) + self.assertEqual(results[0].code, "E501") + self.assertEqual(results[0].message, "Line too long (35 > 10 characters)") + self.assertEqual(results[0].location.row, 4) + self.assertEqual(results[0].location.column, 10) + self.assertEqual(results[0].end_location.row, 4) + self.assertEqual(results[0].end_location.column, 35) + + def test_invalid_options(self): + with self.assertRaisesRegex( + Exception, + "unknown field `line-count`, expected one of `allowed-confusables`, .*", + ): + check("print(123)", options={"line-count": 10}) + if __name__ == "__main__": unittest.main() diff --git a/src/lib_native.rs b/src/lib_native.rs index 8bdfe3d9edaff..bac25f0e2d81d 100644 --- a/src/lib_native.rs +++ b/src/lib_native.rs @@ -15,7 +15,9 @@ use crate::source_code_style::SourceCodeStyleDetector; use crate::{directives, packages, resolver}; /// Load the relevant `Settings` for a given `Path`. -fn resolve(path: &Path) -> Result { +/// TODO(rgerecke): Move this somewhere else as to not make it part of public +/// API +pub fn resolve(path: &Path) -> Result { if let Some(pyproject) = pyproject::find_settings_toml(path)? { // First priority: `pyproject.toml` in the current `Path`. resolver::resolve_settings(&pyproject, &Relativity::Parent, None) diff --git a/src/lib_python.rs b/src/lib_python.rs index 2c4e96dd6fd4f..f0d9d51c2f56e 100644 --- a/src/lib_python.rs +++ b/src/lib_python.rs @@ -1,11 +1,14 @@ use std::path::Path; use anyhow::Result; +use path_absolutize::path_dedot; use pyo3::prelude::*; use pyo3::types::PyString; +use pythonize::depythonize; use rustpython_parser::lexer::LexResult; use crate::checks::{Check, CheckCode}; +use crate::lib_native::resolve; use crate::linter::check_path; use crate::rustpython_helpers::tokenize; use crate::settings::configuration::Configuration; @@ -46,13 +49,18 @@ impl IntoPy for CheckCode { } } -fn inner_check

(contents: &str, path: P) -> Result> -where - P: AsRef, -{ - // let configuration = Configuration::from_options(options, path.as_ref())?; - let configuration = Configuration::default(); - let settings = Settings::from_configuration(configuration, path.as_ref())?; +fn inner_check( + contents: &str, + path: Option<&Path>, + options: Option, +) -> Result> { + let filename = path.unwrap_or_else(|| Path::new("")); + let path = path.unwrap_or(&path_dedot::CWD); + + let settings = match options { + Some(opt) => Settings::from_configuration(Configuration::from_options(opt, path)?, path)?, + None => resolve(path)?, + }; // Tokenize once. let tokens: Vec = tokenize(contents); @@ -68,8 +76,8 @@ where // Generate checks. let checks = check_path( - path.as_ref(), - packages::detect_package_root(path.as_ref()), + filename, + packages::detect_package_root(path), contents, tokens, &locator, @@ -84,26 +92,29 @@ where } #[pyfunction] -fn check(contents: &str, path: Option<&str>) -> PyResult> { - // TODO(rgerecke): Accept settings - Ok( - inner_check(contents, path.unwrap_or("")).map(|r| { - r.iter() - .map(|check| Message { - code: check.kind.code().clone(), - message: check.kind.body(), - location: Location { - row: check.location.row(), - column: check.location.column(), - }, - end_location: Location { - row: check.end_location.row(), - column: check.end_location.column(), - }, - }) - .collect::>() - })?, - ) +fn check(contents: &str, path: Option<&str>, options: Option<&PyAny>) -> PyResult> { + let path = path.map(Path::new); + let options = match options { + Some(v) => depythonize(v)?, + None => None, + }; + + Ok(inner_check(contents, path, options).map(|r| { + r.iter() + .map(|check| Message { + code: check.kind.code().clone(), + message: check.kind.body(), + location: Location { + row: check.location.row(), + column: check.location.column(), + }, + end_location: Location { + row: check.end_location.row(), + column: check.end_location.column(), + }, + }) + .collect::>() + })?) } #[pymodule]