Compare commits

..

6 Commits

Author SHA1 Message Date
Charlie Marsh
4319bd1755 Bump version to 0.0.25 2022-09-03 12:09:11 -04:00
Charlie Marsh
6bb6cb1783 Implement F822 (#94) 2022-09-03 12:08:26 -04:00
Charlie Marsh
e9412c9452 Generate a list of supported lint rules (#93) 2022-09-03 11:56:11 -04:00
Charlie Marsh
94faa7f301 Rename resources/test/src to resources/test/fixtures (#92) 2022-09-03 11:49:03 -04:00
Charlie Marsh
5041f6530c Implement R0205 (#91) 2022-09-03 11:33:54 -04:00
Narawit Rakket
3c7716ef27 refactor: run cargo clippy --fix (#88) 2022-09-02 17:01:24 -04:00
32 changed files with 306 additions and 78 deletions

View File

@@ -1,5 +1,5 @@
repos:
- repo: https://github.com/charliermarsh/ruff
rev: v0.0.24
rev: v0.0.25
hooks:
- id: lint

2
Cargo.lock generated
View File

@@ -1641,7 +1641,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.24"
version = "0.0.25"
dependencies = [
"anyhow",
"bincode",

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.24"
version = "0.0.25"
edition = "2021"
[lib]

View File

@@ -56,7 +56,7 @@ ruff also works with [Pre-Commit](https://pre-commit.com) (requires Cargo on sys
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff
rev: v0.0.24
rev: v0.0.25
hooks:
- id: lint
```
@@ -106,6 +106,25 @@ OPTIONS:
-w, --watch Run in watch mode by re-running whenever files change
```
## Rules
| Code | Name | Message |
| ---- | ----- | ------- |
| E501 | LineTooLong | Line too long |
| F401 | UnusedImport | `...` imported but unused |
| F403 | ImportStarUsage | Unable to detect undefined names |
| F541 | FStringMissingPlaceholders | f-string without any placeholders |
| F634 | IfTuple | If test is a tuple, which is always `True` |
| F704 | YieldOutsideFunction | a `yield` or `yield from` statement outside of a function/method |
| F706 | ReturnOutsideFunction | a `return` statement outside of a function/method |
| F821 | UndefinedName | Undefined name `...` |
| F822 | UndefinedExport | Undefined name `...` in __all__ |
| F823 | UndefinedLocal | Local variable `...` referenced before assignment |
| F831 | DuplicateArgumentName | Duplicate argument name in function definition |
| F841 | UnusedVariable | Local variable `...` is assigned to but never used |
| F901 | RaiseNotImplemented | 'raise NotImplemented' should be 'raise NotImplementedError |
| R0205 | UselessObjectInheritance | Class ... inherits from object |
## Development
ruff is written in Rust (1.63.0). You'll need to install the [Rust toolchain](https://www.rust-lang.org/tools/install)
@@ -114,7 +133,7 @@ for development.
Assuming you have `cargo` installed, you can run:
```shell
cargo run resources/test/src
cargo run resources/test/fixtures
cargo fmt
cargo clippy
cargo test

View File

@@ -0,0 +1,33 @@
/// Generate a Markdown-compatible table of supported lint rules.
use ruff::checks::CheckKind;
fn main() {
let mut check_kinds: Vec<CheckKind> = vec![
CheckKind::DuplicateArgumentName,
CheckKind::FStringMissingPlaceholders,
CheckKind::IfTuple,
CheckKind::ImportStarUsage,
CheckKind::LineTooLong,
CheckKind::RaiseNotImplemented,
CheckKind::ReturnOutsideFunction,
CheckKind::UndefinedLocal("...".to_string()),
CheckKind::UndefinedName("...".to_string()),
CheckKind::UndefinedExport("...".to_string()),
CheckKind::UnusedImport("...".to_string()),
CheckKind::UnusedVariable("...".to_string()),
CheckKind::UselessObjectInheritance("...".to_string()),
CheckKind::YieldOutsideFunction,
];
check_kinds.sort_by_key(|check_kind| check_kind.code());
println!("| Code | Name | Message |");
println!("| ---- | ----- | ------- |");
for check_kind in check_kinds {
println!(
"| {} | {} | {} |",
check_kind.code().as_str(),
check_kind.name(),
check_kind.body()
);
}
}

3
resources/test/fixtures/F822.py vendored Normal file
View File

@@ -0,0 +1,3 @@
a = 1
__all__ = ["a", "b"]

22
resources/test/fixtures/R0205.py vendored Normal file
View File

@@ -0,0 +1,22 @@
class A:
...
class B(object):
...
class C(B, object):
...
def f():
class D(object):
...
object = A
class E(object):
...

View File

@@ -10,8 +10,10 @@ select = [
"F704",
"F706",
"F821",
"F822",
"F823",
"F831",
"F841",
"F901",
"R0205",
]

View File

@@ -1,4 +1,5 @@
use std::collections::BTreeSet;
use std::path::Path;
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Stmt,
@@ -15,6 +16,7 @@ use crate::visitor::{walk_excepthandler, Visitor};
struct Checker<'a> {
settings: &'a Settings,
path: &'a str,
checks: Vec<Check>,
scopes: Vec<Scope>,
dead_scopes: Vec<Scope>,
@@ -24,9 +26,10 @@ struct Checker<'a> {
}
impl Checker<'_> {
pub fn new(settings: &Settings) -> Checker {
pub fn new<'a>(settings: &'a Settings, path: &'a str) -> Checker<'a> {
Checker {
settings,
path,
checks: vec![],
scopes: vec![],
dead_scopes: vec![],
@@ -108,11 +111,37 @@ impl Visitor for Checker<'_> {
}
}
StmtKind::ClassDef {
name,
bases,
keywords,
decorator_list,
..
} => {
if self.settings.select.contains(&CheckCode::R0205) {
for expr in bases {
if let ExprKind::Name { id, .. } = &expr.node {
if id == "object" {
let scope = self.scopes.last().expect("No current scope found.");
match scope.values.get(id) {
None
| Some(Binding {
kind: BindingKind::Builtin,
..
}) => {
self.checks.push(Check {
kind: CheckKind::UselessObjectInheritance(
name.to_string(),
),
location: stmt.location,
});
}
_ => {}
}
}
}
}
}
for expr in bases {
self.visit_expr(expr, Some(stmt))
}
@@ -647,19 +676,40 @@ impl Checker<'_> {
}
fn check_dead_scopes(&mut self) {
if self.settings.select.contains(&CheckCode::F401) {
for scope in &self.dead_scopes {
let all_binding = match scope.values.get("__all__") {
Some(binding) => match &binding.kind {
BindingKind::Export(names) => Some(names),
_ => None,
},
_ => None,
};
if !self.settings.select.contains(&CheckCode::F822)
&& !self.settings.select.contains(&CheckCode::F401)
{
return;
}
for scope in &self.dead_scopes {
let all_binding = scope.values.get("__all__");
let all_names = all_binding.and_then(|binding| match &binding.kind {
BindingKind::Export(names) => Some(names),
_ => None,
});
if self.settings.select.contains(&CheckCode::F822)
&& !Path::new(self.path).ends_with("__init__.py")
{
if let Some(binding) = all_binding {
if let Some(names) = all_names {
for name in names {
if !scope.values.contains_key(name) {
self.checks.push(Check {
kind: CheckKind::UndefinedExport(name.to_string()),
location: binding.location,
});
}
}
}
}
}
if self.settings.select.contains(&CheckCode::F401) {
for (name, binding) in scope.values.iter().rev() {
let used = binding.used.is_some()
|| all_binding
|| all_names
.map(|names| names.contains(name))
.unwrap_or_default();
@@ -682,7 +732,7 @@ impl Checker<'_> {
}
pub fn check_ast(python_ast: &Suite, settings: &Settings, path: &str) -> Vec<Check> {
let mut checker = Checker::new(settings);
let mut checker = Checker::new(settings, path);
checker.push_scope(Scope::new(ScopeKind::Module));
checker.bind_builtins();

View File

@@ -16,10 +16,12 @@ pub enum CheckCode {
F704,
F706,
F821,
F822,
F823,
F831,
F841,
F901,
R0205,
}
impl FromStr for CheckCode {
@@ -35,10 +37,12 @@ impl FromStr for CheckCode {
"F704" => Ok(CheckCode::F704),
"F706" => Ok(CheckCode::F706),
"F821" => Ok(CheckCode::F821),
"F822" => Ok(CheckCode::F822),
"F823" => Ok(CheckCode::F823),
"F831" => Ok(CheckCode::F831),
"F841" => Ok(CheckCode::F841),
"F901" => Ok(CheckCode::F901),
"R0205" => Ok(CheckCode::R0205),
_ => Err(anyhow::anyhow!("Unknown check code: {s}")),
}
}
@@ -56,9 +60,11 @@ impl CheckCode {
CheckCode::F706 => "F706",
CheckCode::F821 => "F821",
CheckCode::F823 => "F823",
CheckCode::F822 => "F822",
CheckCode::F831 => "F831",
CheckCode::F841 => "F841",
CheckCode::F901 => "F901",
CheckCode::R0205 => "R0205",
}
}
@@ -73,10 +79,12 @@ impl CheckCode {
CheckCode::F704 => &LintSource::AST,
CheckCode::F706 => &LintSource::AST,
CheckCode::F821 => &LintSource::AST,
CheckCode::F822 => &LintSource::AST,
CheckCode::F823 => &LintSource::AST,
CheckCode::F831 => &LintSource::AST,
CheckCode::F841 => &LintSource::AST,
CheckCode::F901 => &LintSource::AST,
CheckCode::R0205 => &LintSource::AST,
}
}
}
@@ -97,13 +105,35 @@ pub enum CheckKind {
RaiseNotImplemented,
ReturnOutsideFunction,
UndefinedLocal(String),
UndefinedExport(String),
UndefinedName(String),
UnusedImport(String),
UnusedVariable(String),
UselessObjectInheritance(String),
YieldOutsideFunction,
}
impl CheckKind {
/// The name of the check.
pub fn name(&self) -> &'static str {
match self {
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
CheckKind::IfTuple => "IfTuple",
CheckKind::ImportStarUsage => "ImportStarUsage",
CheckKind::LineTooLong => "LineTooLong",
CheckKind::RaiseNotImplemented => "RaiseNotImplemented",
CheckKind::ReturnOutsideFunction => "ReturnOutsideFunction",
CheckKind::UndefinedLocal(_) => "UndefinedLocal",
CheckKind::UndefinedExport(_) => "UndefinedExport",
CheckKind::UndefinedName(_) => "UndefinedName",
CheckKind::UnusedImport(_) => "UnusedImport",
CheckKind::UnusedVariable(_) => "UnusedVariable",
CheckKind::UselessObjectInheritance(_) => "UselessObjectInheritance",
CheckKind::YieldOutsideFunction => "YieldOutsideFunction",
}
}
/// A four-letter shorthand code for the check.
pub fn code(&self) -> &'static CheckCode {
match self {
@@ -114,10 +144,12 @@ impl CheckKind {
CheckKind::LineTooLong => &CheckCode::E501,
CheckKind::RaiseNotImplemented => &CheckCode::F901,
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
CheckKind::UndefinedExport(_) => &CheckCode::F822,
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
CheckKind::UndefinedName(_) => &CheckCode::F821,
CheckKind::UnusedImport(_) => &CheckCode::F401,
CheckKind::UnusedVariable(_) => &CheckCode::F841,
CheckKind::UselessObjectInheritance(_) => &CheckCode::R0205,
CheckKind::YieldOutsideFunction => &CheckCode::F704,
}
}
@@ -140,6 +172,9 @@ impl CheckKind {
CheckKind::ReturnOutsideFunction => {
"a `return` statement outside of a function/method".to_string()
}
CheckKind::UndefinedExport(name) => {
format!("Undefined name `{name}` in __all__")
}
CheckKind::UndefinedName(name) => {
format!("Undefined name `{name}`")
}
@@ -150,6 +185,9 @@ impl CheckKind {
CheckKind::UnusedVariable(name) => {
format!("Local variable `{name}` is assigned to but never used")
}
CheckKind::UselessObjectInheritance(name) => {
format!("Class {name} inherits from object")
}
CheckKind::YieldOutsideFunction => {
"a `yield` or `yield from` statement outside of a function/method".to_string()
}

View File

@@ -68,7 +68,7 @@ mod tests {
#[test]
fn e501() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/E501.py"),
Path::new("./resources/test/fixtures/E501.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -79,7 +79,7 @@ mod tests {
let expected = vec![Message {
kind: CheckKind::LineTooLong,
location: Location::new(5, 89),
filename: "./resources/test/src/E501.py".to_string(),
filename: "./resources/test/fixtures/E501.py".to_string(),
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
@@ -92,7 +92,7 @@ mod tests {
#[test]
fn f401() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F401.py"),
Path::new("./resources/test/fixtures/F401.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -104,17 +104,17 @@ mod tests {
Message {
kind: CheckKind::UnusedImport("logging.handlers".to_string()),
location: Location::new(12, 1),
filename: "./resources/test/src/F401.py".to_string(),
filename: "./resources/test/fixtures/F401.py".to_string(),
},
Message {
kind: CheckKind::UnusedImport("functools".to_string()),
location: Location::new(3, 1),
filename: "./resources/test/src/F401.py".to_string(),
filename: "./resources/test/fixtures/F401.py".to_string(),
},
Message {
kind: CheckKind::UnusedImport("collections.OrderedDict".to_string()),
location: Location::new(4, 1),
filename: "./resources/test/src/F401.py".to_string(),
filename: "./resources/test/fixtures/F401.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -128,7 +128,7 @@ mod tests {
#[test]
fn f403() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F403.py"),
Path::new("./resources/test/fixtures/F403.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -140,12 +140,12 @@ mod tests {
Message {
kind: CheckKind::ImportStarUsage,
location: Location::new(1, 1),
filename: "./resources/test/src/F403.py".to_string(),
filename: "./resources/test/fixtures/F403.py".to_string(),
},
Message {
kind: CheckKind::ImportStarUsage,
location: Location::new(2, 1),
filename: "./resources/test/src/F403.py".to_string(),
filename: "./resources/test/fixtures/F403.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -158,7 +158,7 @@ mod tests {
#[test]
fn f541() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F541.py"),
Path::new("./resources/test/fixtures/F541.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -170,17 +170,17 @@ mod tests {
Message {
kind: CheckKind::FStringMissingPlaceholders,
location: Location::new(4, 7),
filename: "./resources/test/src/F541.py".to_string(),
filename: "./resources/test/fixtures/F541.py".to_string(),
},
Message {
kind: CheckKind::FStringMissingPlaceholders,
location: Location::new(5, 7),
filename: "./resources/test/src/F541.py".to_string(),
filename: "./resources/test/fixtures/F541.py".to_string(),
},
Message {
kind: CheckKind::FStringMissingPlaceholders,
location: Location::new(7, 7),
filename: "./resources/test/src/F541.py".to_string(),
filename: "./resources/test/fixtures/F541.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -194,7 +194,7 @@ mod tests {
#[test]
fn f634() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F634.py"),
Path::new("./resources/test/fixtures/F634.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -206,12 +206,12 @@ mod tests {
Message {
kind: CheckKind::IfTuple,
location: Location::new(1, 1),
filename: "./resources/test/src/F634.py".to_string(),
filename: "./resources/test/fixtures/F634.py".to_string(),
},
Message {
kind: CheckKind::IfTuple,
location: Location::new(7, 5),
filename: "./resources/test/src/F634.py".to_string(),
filename: "./resources/test/fixtures/F634.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -225,7 +225,7 @@ mod tests {
#[test]
fn f704() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F704.py"),
Path::new("./resources/test/fixtures/F704.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -237,17 +237,17 @@ mod tests {
Message {
kind: CheckKind::YieldOutsideFunction,
location: Location::new(6, 5),
filename: "./resources/test/src/F704.py".to_string(),
filename: "./resources/test/fixtures/F704.py".to_string(),
},
Message {
kind: CheckKind::YieldOutsideFunction,
location: Location::new(9, 1),
filename: "./resources/test/src/F704.py".to_string(),
filename: "./resources/test/fixtures/F704.py".to_string(),
},
Message {
kind: CheckKind::YieldOutsideFunction,
location: Location::new(10, 1),
filename: "./resources/test/src/F704.py".to_string(),
filename: "./resources/test/fixtures/F704.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -261,7 +261,7 @@ mod tests {
#[test]
fn f706() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F706.py"),
Path::new("./resources/test/fixtures/F706.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -273,12 +273,12 @@ mod tests {
Message {
kind: CheckKind::ReturnOutsideFunction,
location: Location::new(6, 5),
filename: "./resources/test/src/F706.py".to_string(),
filename: "./resources/test/fixtures/F706.py".to_string(),
},
Message {
kind: CheckKind::ReturnOutsideFunction,
location: Location::new(9, 1),
filename: "./resources/test/src/F706.py".to_string(),
filename: "./resources/test/fixtures/F706.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -292,7 +292,7 @@ mod tests {
#[test]
fn f821() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F821.py"),
Path::new("./resources/test/fixtures/F821.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -304,22 +304,22 @@ mod tests {
Message {
kind: CheckKind::UndefinedName("self".to_string()),
location: Location::new(2, 12),
filename: "./resources/test/src/F821.py".to_string(),
filename: "./resources/test/fixtures/F821.py".to_string(),
},
Message {
kind: CheckKind::UndefinedName("self".to_string()),
location: Location::new(6, 13),
filename: "./resources/test/src/F821.py".to_string(),
filename: "./resources/test/fixtures/F821.py".to_string(),
},
Message {
kind: CheckKind::UndefinedName("self".to_string()),
location: Location::new(10, 9),
filename: "./resources/test/src/F821.py".to_string(),
filename: "./resources/test/fixtures/F821.py".to_string(),
},
Message {
kind: CheckKind::UndefinedName("numeric_string".to_string()),
location: Location::new(21, 12),
filename: "./resources/test/src/F821.py".to_string(),
filename: "./resources/test/fixtures/F821.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -331,33 +331,21 @@ mod tests {
}
#[test]
fn f831() -> Result<()> {
fn f822() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F831.py"),
Path::new("./resources/test/fixtures/F822.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::F831]),
select: BTreeSet::from([CheckCode::F822]),
},
&cache::Mode::None,
)?;
let expected = vec![
Message {
kind: CheckKind::DuplicateArgumentName,
location: Location::new(1, 25),
filename: "./resources/test/src/F831.py".to_string(),
},
Message {
kind: CheckKind::DuplicateArgumentName,
location: Location::new(5, 28),
filename: "./resources/test/src/F831.py".to_string(),
},
Message {
kind: CheckKind::DuplicateArgumentName,
location: Location::new(9, 27),
filename: "./resources/test/src/F831.py".to_string(),
},
];
let expected = vec![Message {
kind: CheckKind::UndefinedExport("b".to_string()),
location: Location::new(3, 1),
filename: "./resources/test/fixtures/F822.py".to_string(),
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
@@ -369,7 +357,7 @@ mod tests {
#[test]
fn f823() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F823.py"),
Path::new("./resources/test/fixtures/F823.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -380,7 +368,7 @@ mod tests {
let expected = vec![Message {
kind: CheckKind::UndefinedLocal("my_var".to_string()),
location: Location::new(6, 5),
filename: "./resources/test/src/F823.py".to_string(),
filename: "./resources/test/fixtures/F823.py".to_string(),
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
@@ -390,10 +378,46 @@ mod tests {
Ok(())
}
#[test]
fn f831() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/F831.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::F831]),
},
&cache::Mode::None,
)?;
let expected = vec![
Message {
kind: CheckKind::DuplicateArgumentName,
location: Location::new(1, 25),
filename: "./resources/test/fixtures/F831.py".to_string(),
},
Message {
kind: CheckKind::DuplicateArgumentName,
location: Location::new(5, 28),
filename: "./resources/test/fixtures/F831.py".to_string(),
},
Message {
kind: CheckKind::DuplicateArgumentName,
location: Location::new(9, 27),
filename: "./resources/test/fixtures/F831.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn f841() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F841.py"),
Path::new("./resources/test/fixtures/F841.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -405,12 +429,12 @@ mod tests {
Message {
kind: CheckKind::UnusedVariable("e".to_string()),
location: Location::new(3, 1),
filename: "./resources/test/src/F841.py".to_string(),
filename: "./resources/test/fixtures/F841.py".to_string(),
},
Message {
kind: CheckKind::UnusedVariable("z".to_string()),
location: Location::new(16, 5),
filename: "./resources/test/src/F841.py".to_string(),
filename: "./resources/test/fixtures/F841.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
@@ -424,7 +448,7 @@ mod tests {
#[test]
fn f901() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/F901.py"),
Path::new("./resources/test/fixtures/F901.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
@@ -436,12 +460,48 @@ mod tests {
Message {
kind: CheckKind::RaiseNotImplemented,
location: Location::new(2, 5),
filename: "./resources/test/src/F901.py".to_string(),
filename: "./resources/test/fixtures/F901.py".to_string(),
},
Message {
kind: CheckKind::RaiseNotImplemented,
location: Location::new(6, 5),
filename: "./resources/test/src/F901.py".to_string(),
filename: "./resources/test/fixtures/F901.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn r0205() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/R0205.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::R0205]),
},
&cache::Mode::None,
)?;
let expected = vec![
Message {
kind: CheckKind::UselessObjectInheritance("B".to_string()),
location: Location::new(5, 1),
filename: "./resources/test/fixtures/R0205.py".to_string(),
},
Message {
kind: CheckKind::UselessObjectInheritance("C".to_string()),
location: Location::new(9, 1),
filename: "./resources/test/fixtures/R0205.py".to_string(),
},
Message {
kind: CheckKind::UselessObjectInheritance("D".to_string()),
location: Location::new(14, 5),
filename: "./resources/test/fixtures/R0205.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());

View File

@@ -216,18 +216,17 @@ other-attribute = 1
#[test]
fn find_and_parse_pyproject_toml() -> Result<()> {
let project_root = find_project_root([Path::new("resources/test/src/__init__.py")])
let project_root = find_project_root([Path::new("resources/test/fixtures/__init__.py")])
.expect("Unable to find project root.");
assert_eq!(project_root, Path::new("resources/test/src"));
assert_eq!(project_root, Path::new("resources/test/fixtures"));
let path = find_pyproject_toml(&project_root).expect("Unable to find pyproject.toml.");
assert_eq!(path, Path::new("resources/test/src/pyproject.toml"));
assert_eq!(path, Path::new("resources/test/fixtures/pyproject.toml"));
let pyproject = parse_pyproject_toml(&path)?;
let config = pyproject
.tool
.map(|tool| tool.ruff)
.flatten()
.and_then(|tool| tool.ruff)
.expect("Unable to find tool.ruff.");
assert_eq!(
config,
@@ -246,10 +245,12 @@ other-attribute = 1
CheckCode::F704,
CheckCode::F706,
CheckCode::F821,
CheckCode::F822,
CheckCode::F823,
CheckCode::F831,
CheckCode::F841,
CheckCode::F901,
CheckCode::R0205,
])),
}
);