Compare commits

..

8 Commits

Author SHA1 Message Date
Charlie Marsh
5f77b420cd Bump version to 0.0.37 2022-09-12 21:35:08 -04:00
Charlie Marsh
90f9e60517 Implement F722 (#175) 2022-09-12 21:34:27 -04:00
Brian Okken
320737f6e4 Change URL to comply with PEP 621 (#173) 2022-09-12 18:38:20 -04:00
Charlie Marsh
dfba1416b2 Implement F406 (#172) 2022-09-12 16:47:30 -04:00
Charlie Marsh
2ca3f35bd1 Remove one match from checks.rs 2022-09-12 16:32:27 -04:00
Adrian Garcia Badaracco
b1c40d5fa7 Run MacOS builds in parallel (#171) 2022-09-12 16:24:13 -04:00
Charlie Marsh
a129e27b3e Tweak rule counts 2022-09-12 15:26:21 -04:00
Charlie Marsh
ad7daa008e Update README to enumerate missing Flake8 rules 2022-09-12 15:24:32 -04:00
15 changed files with 175 additions and 61 deletions

View File

@@ -16,7 +16,7 @@ env:
PYTHON_VERSION: "3.7" # to build abi3 wheels
jobs:
macos:
macos-x86_64:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
@@ -39,6 +39,25 @@ jobs:
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
macos-universal:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Build wheels - universal2
uses: messense/maturin-action@v1
with:
@@ -261,7 +280,8 @@ jobs:
name: Release
runs-on: ubuntu-latest
needs:
- macos
- macos-universal
- macos-x86_64
- windows
- linux
- linux-cross

View File

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

2
Cargo.lock generated
View File

@@ -1744,7 +1744,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.36"
version = "0.0.37"
dependencies = [
"anyhow",
"bincode",

View File

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

View File

@@ -57,7 +57,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.36
rev: v0.0.37
hooks:
- id: lint
```
@@ -86,7 +86,7 @@ ruff path/to/code/ --select F401 F403
See `ruff --help` for more:
```shell
ruff (v0.0.36)
ruff (v0.0.37)
An extremely fast Python linter.
USAGE:
@@ -123,17 +123,15 @@ ruff's goal is to achieve feature-parity with Flake8 when used (1) without any p
(2) alongside Black, and (3) on Python 3 code. (Using Black obviates the need for many of Flake8's
stylistic checks; limiting to Python 3 obviates the need for certain compatibility checks.)
Under those conditions, Flake8 implements about 58 rules, give or take. At time of writing, ruff
implements 36 rules. (Note that these 36 rules likely cover a disproportionate share of errors:
Under those conditions, Flake8 implements about 60 rules, give or take. At time of writing, ruff
implements 38 rules. (Note that these 38 rules likely cover a disproportionate share of errors:
unused imports, undefined variables, etc.)
Of the unimplemented rules, ruff is missing:
The unimplemented rules are tracked in #170, and include:
- 15 rules related to string `.format` calls.
- 3 rules related to syntax errors in doctests and annotations.
- 2 rules related to redefining unused names.
...along with a variety of others that don't fit neatly into any specific category.
- 14 rules related to string `.format` calls.
- 1 rule related to parsing and syntax.
- 6 logical rules.
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
@@ -158,8 +156,9 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
| E743 | AmbiguousFunctionName | ambiguous function name '...' |
| E902 | IOError | No such file or directory: `...` |
| F401 | UnusedImport | `...` imported but unused |
| F403 | ImportStarUsage | Unable to detect undefined names |
| F403 | ImportStarUsage | `from ... import *` used; unable to detect undefined names |
| F404 | LateFutureImport | from __future__ imports must occur at the beginning of the file |
| F406 | ImportStarNotPermitted | `from ... import *` only allowed at module level |
| F407 | FutureFeatureNotDefined | future feature '...' is not defined |
| F541 | FStringMissingPlaceholders | f-string without any placeholders |
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated |
@@ -173,6 +172,7 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
| F704 | YieldOutsideFunction | a `yield` or `yield from` statement outside of a function/method |
| F706 | ReturnOutsideFunction | a `return` statement outside of a function/method |
| F707 | DefaultExceptNotLast | an `except:` block as not the last exception handler |
| F722 | ForwardAnnotationSyntaxError | syntax error in forward annotation '...' |
| F821 | UndefinedName | Undefined name `...` |
| F822 | UndefinedExport | Undefined name `...` in `__all__` |
| F823 | UndefinedLocal | Local variable `...` referenced before assignment |

View File

@@ -13,11 +13,13 @@ fn main() {
CheckKind::DoNotAssignLambda,
CheckKind::DoNotUseBareExcept,
CheckKind::DuplicateArgumentName,
CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
CheckKind::FStringMissingPlaceholders,
CheckKind::FutureFeatureNotDefined("...".to_string()),
CheckKind::IOError("...".to_string()),
CheckKind::IfTuple,
CheckKind::ImportStarUsage,
CheckKind::ImportStarNotPermitted("...".to_string()),
CheckKind::ImportStarUsage("...".to_string()),
CheckKind::LateFutureImport,
CheckKind::LineTooLong(89, 88),
CheckKind::ModuleImportNotAtTopOfFile,

View File

@@ -19,10 +19,12 @@ classifiers = [
]
author = "Charlie Marsh"
author_email = "charlie.r.marsh@gmail.com"
url = "https://github.com/charliermarsh/ruff"
description = "An extremely fast Python linter, written in Rust."
requires-python = ">=3.7"
[project.urls]
repository = "https://github.com/charliermarsh/ruff"
[build-system]
requires = ["maturin>=0.13,<0.14"]
build-backend = "maturin"

9
resources/test/fixtures/F406.py vendored Normal file
View File

@@ -0,0 +1,9 @@
from F634 import *
def f():
from F634 import *
class F:
from F634 import *

10
resources/test/fixtures/F722.py vendored Normal file
View File

@@ -0,0 +1,10 @@
class A:
pass
def f() -> "A":
pass
def g() -> "///":
pass

View File

@@ -17,6 +17,7 @@ select = [
"F401",
"F403",
"F404",
"F406",
"F407",
"F541",
"F601",
@@ -30,6 +31,7 @@ select = [
"F704",
"F706",
"F707",
"F722",
"F821",
"F822",
"F823",

View File

@@ -448,8 +448,25 @@ where
);
if self.settings.select.contains(&CheckCode::F403) {
self.checks
.push(Check::new(CheckKind::ImportStarUsage, stmt.location));
self.checks.push(Check::new(
CheckKind::ImportStarUsage(
module.clone().unwrap_or_else(|| "module".to_string()),
),
stmt.location,
));
}
if self.settings.select.contains(&CheckCode::F406) {
let scope = &self.scopes
[*(self.scope_stack.last().expect("No current scope found."))];
if !matches!(scope.kind, ScopeKind::Module) {
self.checks.push(Check::new(
CheckKind::ImportStarNotPermitted(
module.clone().unwrap_or_else(|| "module".to_string()),
),
stmt.location,
));
}
}
} else {
let binding = Binding {
@@ -1164,6 +1181,11 @@ impl<'a> Checker<'a> {
if let Ok(mut expr) = parser::parse_expression(expression, path) {
relocate_expr(&mut expr, location);
allocator.push(expr);
} else if self.settings.select.contains(&CheckCode::F722) {
self.checks.push(Check::new(
CheckKind::ForwardAnnotationSyntaxError(expression.to_string()),
location,
));
}
}
for expr in allocator {

View File

@@ -23,6 +23,7 @@ pub enum CheckCode {
F401,
F403,
F404,
F406,
F407,
F541,
F601,
@@ -36,6 +37,7 @@ pub enum CheckCode {
F704,
F706,
F707,
F722,
F821,
F822,
F823,
@@ -66,6 +68,7 @@ impl FromStr for CheckCode {
"F401" => Ok(CheckCode::F401),
"F403" => Ok(CheckCode::F403),
"F404" => Ok(CheckCode::F404),
"F406" => Ok(CheckCode::F406),
"F407" => Ok(CheckCode::F407),
"F541" => Ok(CheckCode::F541),
"F601" => Ok(CheckCode::F601),
@@ -110,6 +113,7 @@ impl CheckCode {
CheckCode::F401 => "F401",
CheckCode::F403 => "F403",
CheckCode::F404 => "F404",
CheckCode::F406 => "F406",
CheckCode::F407 => "F407",
CheckCode::F541 => "F541",
CheckCode::F601 => "F601",
@@ -123,6 +127,7 @@ impl CheckCode {
CheckCode::F704 => "F704",
CheckCode::F706 => "F706",
CheckCode::F707 => "F707",
CheckCode::F722 => "F722",
CheckCode::F821 => "F821",
CheckCode::F822 => "F822",
CheckCode::F823 => "F823",
@@ -134,45 +139,12 @@ impl CheckCode {
}
}
/// The source for the check (either the AST, or the physical lines).
/// The source for the check (either the AST, the filesystem, or the physical lines).
pub fn lint_source(&self) -> &'static LintSource {
match self {
CheckCode::E402 => &LintSource::AST,
CheckCode::E501 => &LintSource::Lines,
CheckCode::E711 => &LintSource::AST,
CheckCode::E712 => &LintSource::AST,
CheckCode::E713 => &LintSource::AST,
CheckCode::E714 => &LintSource::AST,
CheckCode::E722 => &LintSource::AST,
CheckCode::E731 => &LintSource::AST,
CheckCode::E741 => &LintSource::AST,
CheckCode::E742 => &LintSource::AST,
CheckCode::E743 => &LintSource::AST,
CheckCode::E902 => &LintSource::FileSystem,
CheckCode::F401 => &LintSource::AST,
CheckCode::F403 => &LintSource::AST,
CheckCode::F404 => &LintSource::AST,
CheckCode::F407 => &LintSource::AST,
CheckCode::F541 => &LintSource::AST,
CheckCode::F601 => &LintSource::AST,
CheckCode::F602 => &LintSource::AST,
CheckCode::F621 => &LintSource::AST,
CheckCode::F622 => &LintSource::AST,
CheckCode::F631 => &LintSource::AST,
CheckCode::F634 => &LintSource::AST,
CheckCode::F701 => &LintSource::AST,
CheckCode::F702 => &LintSource::AST,
CheckCode::F704 => &LintSource::AST,
CheckCode::F706 => &LintSource::AST,
CheckCode::F707 => &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::R001 => &LintSource::AST,
CheckCode::R002 => &LintSource::AST,
_ => &LintSource::AST,
}
}
}
@@ -202,11 +174,13 @@ pub enum CheckKind {
DoNotAssignLambda,
DoNotUseBareExcept,
DuplicateArgumentName,
ForwardAnnotationSyntaxError(String),
FStringMissingPlaceholders,
FutureFeatureNotDefined(String),
IOError(String),
IfTuple,
ImportStarUsage,
ImportStarNotPermitted(String),
ImportStarUsage(String),
LateFutureImport,
LineTooLong(usize, usize),
ModuleImportNotAtTopOfFile,
@@ -244,11 +218,13 @@ impl CheckKind {
CheckKind::DoNotAssignLambda => "DoNotAssignLambda",
CheckKind::DoNotUseBareExcept => "DoNotUseBareExcept",
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
CheckKind::ForwardAnnotationSyntaxError(_) => "ForwardAnnotationSyntaxError",
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
CheckKind::FutureFeatureNotDefined(_) => "FutureFeatureNotDefined",
CheckKind::IOError(_) => "IOError",
CheckKind::IfTuple => "IfTuple",
CheckKind::ImportStarUsage => "ImportStarUsage",
CheckKind::ImportStarNotPermitted(_) => "ImportStarNotPermitted",
CheckKind::ImportStarUsage(_) => "ImportStarUsage",
CheckKind::LateFutureImport => "LateFutureImport",
CheckKind::LineTooLong(_, _) => "LineTooLong",
CheckKind::ModuleImportNotAtTopOfFile => "ModuleImportNotAtTopOfFile",
@@ -288,11 +264,13 @@ impl CheckKind {
CheckKind::DoNotAssignLambda => &CheckCode::E731,
CheckKind::DoNotUseBareExcept => &CheckCode::E722,
CheckKind::DuplicateArgumentName => &CheckCode::F831,
CheckKind::ForwardAnnotationSyntaxError(_) => &CheckCode::F722,
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
CheckKind::IOError(_) => &CheckCode::E902,
CheckKind::IfTuple => &CheckCode::F634,
CheckKind::ImportStarUsage => &CheckCode::F403,
CheckKind::ImportStarNotPermitted(_) => &CheckCode::F406,
CheckKind::ImportStarUsage(_) => &CheckCode::F403,
CheckKind::LateFutureImport => &CheckCode::F404,
CheckKind::LineTooLong(_, _) => &CheckCode::E501,
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
@@ -344,6 +322,9 @@ impl CheckKind {
CheckKind::DuplicateArgumentName => {
"Duplicate argument name in function definition".to_string()
}
CheckKind::ForwardAnnotationSyntaxError(body) => {
format!("syntax error in forward annotation '{body}'")
}
CheckKind::FStringMissingPlaceholders => {
"f-string without any placeholders".to_string()
}
@@ -354,7 +335,12 @@ impl CheckKind {
format!("No such file or directory: `{name}`")
}
CheckKind::IfTuple => "If test is a tuple, which is always `True`".to_string(),
CheckKind::ImportStarUsage => "Unable to detect undefined names".to_string(),
CheckKind::ImportStarNotPermitted(name) => {
format!("`from {name} import *` only allowed at module level")
}
CheckKind::ImportStarUsage(name) => {
format!("`from {name} import *` used; unable to detect undefined names")
}
CheckKind::LateFutureImport => {
"from __future__ imports must occur at the beginning of the file".to_string()
}

View File

@@ -598,12 +598,12 @@ mod tests {
actual.sort_by_key(|check| check.location);
let expected = vec![
Check {
kind: CheckKind::ImportStarUsage,
kind: CheckKind::ImportStarUsage("F634".to_string()),
location: Location::new(1, 1),
fix: None,
},
Check {
kind: CheckKind::ImportStarUsage,
kind: CheckKind::ImportStarUsage("F634".to_string()),
location: Location::new(2, 1),
fix: None,
},
@@ -641,6 +641,38 @@ mod tests {
Ok(())
}
#[test]
fn f406() -> Result<()> {
let mut actual = check_path(
Path::new("./resources/test/fixtures/F406.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::F406]),
},
&fixer::Mode::Generate,
)?;
actual.sort_by_key(|check| check.location);
let expected = vec![
Check {
kind: CheckKind::ImportStarNotPermitted("F634".to_string()),
location: Location::new(5, 5),
fix: None,
},
Check {
kind: CheckKind::ImportStarNotPermitted("F634".to_string()),
location: Location::new(9, 5),
fix: None,
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn f407() -> Result<()> {
let mut actual = check_path(
@@ -1041,6 +1073,31 @@ mod tests {
Ok(())
}
#[test]
fn f722() -> Result<()> {
let mut actual = check_path(
Path::new("./resources/test/fixtures/F722.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::F722]),
},
&fixer::Mode::Generate,
)?;
actual.sort_by_key(|check| check.location);
let expected = vec![Check {
kind: CheckKind::ForwardAnnotationSyntaxError("///".to_string()),
location: Location::new(9, 13),
fix: None,
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn f821() -> Result<()> {
let mut actual = check_path(

View File

@@ -274,6 +274,7 @@ other-attribute = 1
CheckCode::F401,
CheckCode::F403,
CheckCode::F404,
CheckCode::F406,
CheckCode::F407,
CheckCode::F541,
CheckCode::F601,
@@ -287,6 +288,7 @@ other-attribute = 1
CheckCode::F704,
CheckCode::F706,
CheckCode::F707,
CheckCode::F722,
CheckCode::F821,
CheckCode::F822,
CheckCode::F823,

View File

@@ -58,6 +58,7 @@ impl Settings {
CheckCode::E902,
CheckCode::F401,
CheckCode::F403,
CheckCode::F406,
CheckCode::F407,
CheckCode::F541,
CheckCode::F601,
@@ -71,6 +72,7 @@ impl Settings {
CheckCode::F704,
CheckCode::F706,
CheckCode::F707,
CheckCode::F722,
CheckCode::F821,
CheckCode::F822,
CheckCode::F823,