Add documentation for mccabe, isort, and flake8-annotations (#2691)

This commit is contained in:
Charlie Marsh 2023-02-09 11:56:18 -05:00 committed by GitHub
parent 8a98cfc4b8
commit 7d5fb0de8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 713 additions and 23 deletions

View file

@ -748,7 +748,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| C901 | function-is-too-complex | `{name}` is too complex ({complexity}) | |
| C901 | [function-is-too-complex](https://github.com/charliermarsh/ruff/blob/main/docs/rules/function-is-too-complex.md) | `{name}` is too complex ({complexity}) | |
### isort (I)
@ -756,8 +756,8 @@ For more, see [isort](https://pypi.org/project/isort/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| I001 | unsorted-imports | Import block is un-sorted or un-formatted | 🛠 |
| I002 | missing-required-import | Missing required import: `{name}` | 🛠 |
| I001 | [unsorted-imports](https://github.com/charliermarsh/ruff/blob/main/docs/rules/unsorted-imports.md) | Import block is un-sorted or un-formatted | 🛠 |
| I002 | [missing-required-import](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-required-import.md) | Missing required import: `{name}` | 🛠 |
### pep8-naming (N)
@ -899,17 +899,17 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/)
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| ANN001 | missing-type-function-argument | Missing type annotation for function argument `{name}` | |
| ANN002 | missing-type-args | Missing type annotation for `*{name}` | |
| ANN003 | missing-type-kwargs | Missing type annotation for `**{name}` | |
| ANN101 | missing-type-self | Missing type annotation for `{name}` in method | |
| ANN102 | missing-type-cls | Missing type annotation for `{name}` in classmethod | |
| ANN201 | missing-return-type-public-function | Missing return type annotation for public function `{name}` | |
| ANN202 | missing-return-type-private-function | Missing return type annotation for private function `{name}` | |
| ANN204 | missing-return-type-special-method | Missing return type annotation for special method `{name}` | 🛠 |
| ANN205 | missing-return-type-static-method | Missing return type annotation for staticmethod `{name}` | |
| ANN206 | missing-return-type-class-method | Missing return type annotation for classmethod `{name}` | |
| ANN401 | dynamically-typed-expression | Dynamically typed expressions (typing.Any) are disallowed in `{name}` | |
| ANN001 | [missing-type-function-argument](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-type-function-argument.md) | Missing type annotation for function argument `{name}` | |
| ANN002 | [missing-type-args](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-type-args.md) | Missing type annotation for `*{name}` | |
| ANN003 | [missing-type-kwargs](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-type-kwargs.md) | Missing type annotation for `**{name}` | |
| ANN101 | [missing-type-self](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-type-self.md) | Missing type annotation for `{name}` in method | |
| ANN102 | [missing-type-cls](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-type-cls.md) | Missing type annotation for `{name}` in classmethod | |
| ANN201 | [missing-return-type-public-function](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-return-type-public-function.md) | Missing return type annotation for public function `{name}` | |
| ANN202 | [missing-return-type-private-function](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-return-type-private-function.md) | Missing return type annotation for private function `{name}` | |
| ANN204 | [missing-return-type-special-method](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-return-type-special-method.md) | Missing return type annotation for special method `{name}` | 🛠 |
| ANN205 | [missing-return-type-static-method](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-return-type-static-method.md) | Missing return type annotation for staticmethod `{name}` | |
| ANN206 | [missing-return-type-class-method](https://github.com/charliermarsh/ruff/blob/main/docs/rules/missing-return-type-class-method.md) | Missing return type annotation for classmethod `{name}` | |
| ANN401 | [dynamically-typed-expression](https://github.com/charliermarsh/ruff/blob/main/docs/rules/dynamically-typed-expression.md) | Dynamically typed expressions (typing.Any) are disallowed in `{name}` | |
### flake8-bandit (S)

View file

@ -16,6 +16,25 @@ use crate::visibility;
use crate::visibility::Visibility;
define_violation!(
/// ### What it does
/// Checks that function arguments have type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the types of function arguments. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any provided arguments match expectation.
///
/// ### Example
/// ```python
/// def foo(x):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def foo(x: int):
/// ...
/// ```
pub struct MissingTypeFunctionArgument {
pub name: String,
}
@ -29,6 +48,25 @@ impl Violation for MissingTypeFunctionArgument {
}
define_violation!(
/// ### What it does
/// Checks that function `*args` arguments have type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the types of function arguments. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any provided arguments match expectation.
///
/// ### Example
/// ```python
/// def foo(*args):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def foo(*args: int):
/// ...
/// ```
pub struct MissingTypeArgs {
pub name: String,
}
@ -42,6 +80,25 @@ impl Violation for MissingTypeArgs {
}
define_violation!(
/// ### What it does
/// Checks that function `**kwargs` arguments have type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the types of function arguments. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any provided arguments match expectation.
///
/// ### Example
/// ```python
/// def foo(**kwargs):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def foo(**kwargs: int):
/// ...
/// ```
pub struct MissingTypeKwargs {
pub name: String,
}
@ -55,6 +112,30 @@ impl Violation for MissingTypeKwargs {
}
define_violation!(
/// ### What it does
/// Checks that instance method `self` arguments have type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the types of function arguments. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any provided arguments match expectation.
///
/// Note that many type checkers will infer the type of `self` automatically, so this
/// annotation is not strictly necessary.
///
/// ### Example
/// ```python
/// class Foo:
/// def bar(self):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// class Foo:
/// def bar(self: "Foo"):
/// ...
/// ```
pub struct MissingTypeSelf {
pub name: String,
}
@ -68,6 +149,32 @@ impl Violation for MissingTypeSelf {
}
define_violation!(
/// ### What it does
/// Checks that class method `cls` arguments have type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the types of function arguments. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any provided arguments match expectation.
///
/// Note that many type checkers will infer the type of `cls` automatically, so this
/// annotation is not strictly necessary.
///
/// ### Example
/// ```python
/// class Foo:
/// @classmethod
/// def bar(cls):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// class Foo:
/// @classmethod
/// def bar(cls: Type["Foo"]):
/// ...
/// ```
pub struct MissingTypeCls {
pub name: String,
}
@ -81,6 +188,25 @@ impl Violation for MissingTypeCls {
}
define_violation!(
/// ### What it does
/// Checks that public functions and methods have return type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the return types of functions. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any returned values, and the types expected by callers, match expectation.
///
/// ### Example
/// ```python
/// def add(a, b):
/// return a + b
/// ```
///
/// Use instead:
/// ```python
/// def add(a: int, b: int) -> int:
/// return a + b
/// ```
pub struct MissingReturnTypePublicFunction {
pub name: String,
}
@ -94,6 +220,25 @@ impl Violation for MissingReturnTypePublicFunction {
}
define_violation!(
/// ### What it does
/// Checks that private functions and methods have return type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the return types of functions. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any returned values, and the types expected by callers, match expectation.
///
/// ### Example
/// ```python
/// def _add(a, b):
/// return a + b
/// ```
///
/// Use instead:
/// ```python
/// def _add(a: int, b: int) -> int:
/// return a + b
/// ```
pub struct MissingReturnTypePrivateFunction {
pub name: String,
}
@ -107,6 +252,38 @@ impl Violation for MissingReturnTypePrivateFunction {
}
define_violation!(
/// ### What it does
/// Checks that "special" methods, like `__init__`, `__new__`, and `__call__`, have
/// return type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the return types of functions. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any returned values, and the types expected by callers, match expectation.
///
/// Note that type checkers often allow you to omit the return type annotation for
/// `__init__` methods, as long as at least one argument has a type annotation. To
/// opt-in to this behavior, use the `mypy-init-return` setting in your `pyproject.toml`
/// or `ruff.toml` file:
///
/// ```toml
/// [tool.ruff.flake8-annotations]
/// mypy-init-return = true
/// ```
///
/// ### Example
/// ```python
/// class Foo:
/// def __init__(self, x: int):
/// self.x = x
/// ```
///
/// Use instead:
/// ```python
/// class Foo:
/// def __init__(self, x: int) -> None:
/// self.x = x
/// ```
pub struct MissingReturnTypeSpecialMethod {
pub name: String,
}
@ -124,6 +301,29 @@ impl AlwaysAutofixableViolation for MissingReturnTypeSpecialMethod {
}
define_violation!(
/// ### What it does
/// Checks that static methods have return type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the return types of functions. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any returned values, and the types expected by callers, match expectation.
///
/// ### Example
/// ```python
/// class Foo:
/// @staticmethod
/// def bar():
/// return 1
/// ```
///
/// Use instead:
/// ```python
/// class Foo:
/// @staticmethod
/// def bar() -> int:
/// return 1
/// ```
pub struct MissingReturnTypeStaticMethod {
pub name: String,
}
@ -137,6 +337,29 @@ impl Violation for MissingReturnTypeStaticMethod {
}
define_violation!(
/// ### What it does
/// Checks that class methods have return type annotations.
///
/// ### Why is this bad?
/// Type annotations are a good way to document the return types of functions. They also
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
/// any returned values, and the types expected by callers, match expectation.
///
/// ### Example
/// ```python
/// class Foo:
/// @classmethod
/// def bar(cls):
/// return 1
/// ```
///
/// Use instead:
/// ```python
/// class Foo:
/// @classmethod
/// def bar(cls) -> int:
/// return 1
/// ```
pub struct MissingReturnTypeClassMethod {
pub name: String,
}
@ -150,6 +373,25 @@ impl Violation for MissingReturnTypeClassMethod {
}
define_violation!(
/// ### What it does
/// Checks that an expression is annotated with a more specific type than `Any`.
///
/// ### Why is this bad?
/// `Any` is a type that can be anything, and it is the default type for
/// unannotated expressions. It is better to be explicit about the type of an
/// expression, and to use `Any` only when it is really needed.
///
/// ### Example
/// ```python
/// def foo(x: Any):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// def foo(x: int):
/// ...
/// ```
pub struct DynamicallyTypedExpression {
pub name: String,
}

View file

@ -15,6 +15,26 @@ use crate::source_code::{Locator, Stylist};
use crate::violation::AlwaysAutofixableViolation;
define_violation!(
/// ### What it does
/// Adds any required imports, as specified by the user, to the top of the file.
///
/// ### Why is this bad?
/// In some projects, certain imports are required to be present in all files. For
/// example, some projects assume that `from __future__ import annotations` is enabled,
/// and thus require that import to be present in all files. Omitting a "required" import
/// (as specified by the user) can cause errors or unexpected behavior.
///
/// ### Example
/// ```python
/// import typing
/// ```
///
/// Use instead:
/// ```python
/// from __future__ import annotations
///
/// import typing
/// ```
pub struct MissingRequiredImport(pub String);
);
impl AlwaysAutofixableViolation for MissingRequiredImport {

View file

@ -19,6 +19,24 @@ use crate::source_code::{Indexer, Locator, Stylist};
use crate::violation::AlwaysAutofixableViolation;
define_violation!(
/// ### What it does
/// De-duplicates, groups, and sorts imports based on the provided `isort` settings.
///
/// ### Why is this bad?
/// Consistency is good. Use a common convention for imports to make your code
/// more readable and idiomatic.
///
/// ### Example
/// ```python
/// import pandas
/// import numpy as np
/// ```
///
/// Use instead:
/// ```python
/// import numpy as np
/// import pandas
/// ```
pub struct UnsortedImports;
);
impl AlwaysAutofixableViolation for UnsortedImports {

View file

@ -7,6 +7,43 @@ use crate::source_code::Locator;
use crate::violation::Violation;
define_violation!(
/// ### What it does
/// Checks for functions with a high `McCabe` complexity.
///
/// The `McCabe` complexity of a function is a measure of the complexity of the
/// control flow graph of the function. It is calculated by adding one to the
/// number of decision points in the function. A decision point is a place in
/// the code where the program has a choice of two or more paths to follow.
///
/// ### Why is this bad?
/// Functions with a high complexity are hard to understand and maintain.
///
/// ### Example
/// ```python
/// def foo(a, b, c):
/// if a:
/// if b:
/// if c:
/// return 1
/// else:
/// return 2
/// else:
/// return 3
/// else:
/// return 4
/// ```
///
/// Use instead:
/// ```python
/// def foo(a, b, c):
/// if not a:
/// return 4
/// if not b:
/// return 3
/// if not c:
/// return 2
/// return 1
/// ```
pub struct FunctionIsTooComplex {
pub name: String,
pub complexity: usize,

View file

@ -30,20 +30,13 @@ impl Parse for LintMeta {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let mut in_code = false;
let mut explanation = String::new();
for attr in &attrs {
if let Some(lit) = parse_attr(["doc"], attr) {
let value = lit.value();
let line = value.strip_prefix(' ').unwrap_or(&value);
if line.starts_with("```") {
explanation += line;
explanation.push_str(line);
explanation.push('\n');
in_code = !in_code;
} else if !(in_code && line.starts_with("# ")) {
explanation += line;
explanation.push('\n');
}
} else {
return Err(Error::new_spanned(attr, "unexpected attribute"));
}

View file

@ -13,4 +13,5 @@ It should be removed.
### Example
```python
# print('foo')
```

View file

@ -0,0 +1,23 @@
# dynamically-typed-expression (ANN401)
Derived from the **flake8-annotations** linter.
### What it does
Checks that an expression is annotated with a more specific type than `Any`.
### Why is this bad?
`Any` is a type that can be anything, and it is the default type for
unannotated expressions. It is better to be explicit about the type of an
expression, and to use `Any` only when it is really needed.
### Example
```python
def foo(x: Any):
...
```
Use instead:
```python
def foo(x: int):
...
```

View file

@ -0,0 +1,41 @@
# function-is-too-complex (C901)
Derived from the **mccabe** linter.
### What it does
Checks for functions with a high `McCabe` complexity.
The `McCabe` complexity of a function is a measure of the complexity of the
control flow graph of the function. It is calculated by adding one to the
number of decision points in the function. A decision point is a place in
the code where the program has a choice of two or more paths to follow.
### Why is this bad?
Functions with a high complexity are hard to understand and maintain.
### Example
```python
def foo(a, b, c):
if a:
if b:
if c:
return 1
else:
return 2
else:
return 3
else:
return 4
```
Use instead:
```python
def foo(a, b, c):
if not a:
return 4
if not b:
return 3
if not c:
return 2
return 1
```

View file

@ -0,0 +1,26 @@
# missing-required-import (I002)
Derived from the **isort** linter.
Autofix is always available.
### What it does
Adds any required imports, as specified by the user, to the top of the file.
### Why is this bad?
In some projects, certain imports are required to be present in all files. For
example, some projects assume that `from __future__ import annotations` is enabled,
and thus require that import to be present in all files. Omitting a "required" import
(as specified by the user) can cause errors or unexpected behavior.
### Example
```python
import typing
```
Use instead:
```python
from __future__ import annotations
import typing
```

View file

@ -0,0 +1,27 @@
# missing-return-type-class-method (ANN206)
Derived from the **flake8-annotations** linter.
### What it does
Checks that class methods have return type annotations.
### Why is this bad?
Type annotations are a good way to document the return types of functions. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any returned values, and the types expected by callers, match expectation.
### Example
```python
class Foo:
@classmethod
def bar(cls):
return 1
```
Use instead:
```python
class Foo:
@classmethod
def bar(cls) -> int:
return 1
```

View file

@ -0,0 +1,23 @@
# missing-return-type-private-function (ANN202)
Derived from the **flake8-annotations** linter.
### What it does
Checks that private functions and methods have return type annotations.
### Why is this bad?
Type annotations are a good way to document the return types of functions. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any returned values, and the types expected by callers, match expectation.
### Example
```python
def _add(a, b):
return a + b
```
Use instead:
```python
def _add(a: int, b: int) -> int:
return a + b
```

View file

@ -0,0 +1,23 @@
# missing-return-type-public-function (ANN201)
Derived from the **flake8-annotations** linter.
### What it does
Checks that public functions and methods have return type annotations.
### Why is this bad?
Type annotations are a good way to document the return types of functions. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any returned values, and the types expected by callers, match expectation.
### Example
```python
def add(a, b):
return a + b
```
Use instead:
```python
def add(a: int, b: int) -> int:
return a + b
```

View file

@ -0,0 +1,38 @@
# missing-return-type-special-method (ANN204)
Derived from the **flake8-annotations** linter.
Autofix is always available.
### What it does
Checks that "special" methods, like `__init__`, `__new__`, and `__call__`, have
return type annotations.
### Why is this bad?
Type annotations are a good way to document the return types of functions. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any returned values, and the types expected by callers, match expectation.
Note that type checkers often allow you to omit the return type annotation for
`__init__` methods, as long as at least one argument has a type annotation. To
opt-in to this behavior, use the `mypy-init-return` setting in your `pyproject.toml`
or `ruff.toml` file:
```toml
[tool.ruff.flake8-annotations]
mypy-init-return = true
```
### Example
```python
class Foo:
def __init__(self, x: int):
self.x = x
```
Use instead:
```python
class Foo:
def __init__(self, x: int) -> None:
self.x = x
```

View file

@ -0,0 +1,27 @@
# missing-return-type-static-method (ANN205)
Derived from the **flake8-annotations** linter.
### What it does
Checks that static methods have return type annotations.
### Why is this bad?
Type annotations are a good way to document the return types of functions. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any returned values, and the types expected by callers, match expectation.
### Example
```python
class Foo:
@staticmethod
def bar():
return 1
```
Use instead:
```python
class Foo:
@staticmethod
def bar() -> int:
return 1
```

View file

@ -0,0 +1,23 @@
# missing-type-args (ANN002)
Derived from the **flake8-annotations** linter.
### What it does
Checks that function `*args` arguments have type annotations.
### Why is this bad?
Type annotations are a good way to document the types of function arguments. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any provided arguments match expectation.
### Example
```python
def foo(*args):
...
```
Use instead:
```python
def foo(*args: int):
...
```

View file

@ -0,0 +1,30 @@
# missing-type-cls (ANN102)
Derived from the **flake8-annotations** linter.
### What it does
Checks that class method `cls` arguments have type annotations.
### Why is this bad?
Type annotations are a good way to document the types of function arguments. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any provided arguments match expectation.
Note that many type checkers will infer the type of `cls` automatically, so this
annotation is not strictly necessary.
### Example
```python
class Foo:
@classmethod
def bar(cls):
...
```
Use instead:
```python
class Foo:
@classmethod
def bar(cls: Type["Foo"]):
...
```

View file

@ -0,0 +1,23 @@
# missing-type-function-argument (ANN001)
Derived from the **flake8-annotations** linter.
### What it does
Checks that function arguments have type annotations.
### Why is this bad?
Type annotations are a good way to document the types of function arguments. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any provided arguments match expectation.
### Example
```python
def foo(x):
...
```
Use instead:
```python
def foo(x: int):
...
```

View file

@ -0,0 +1,23 @@
# missing-type-kwargs (ANN003)
Derived from the **flake8-annotations** linter.
### What it does
Checks that function `**kwargs` arguments have type annotations.
### Why is this bad?
Type annotations are a good way to document the types of function arguments. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any provided arguments match expectation.
### Example
```python
def foo(**kwargs):
...
```
Use instead:
```python
def foo(**kwargs: int):
...
```

View file

@ -0,0 +1,28 @@
# missing-type-self (ANN101)
Derived from the **flake8-annotations** linter.
### What it does
Checks that instance method `self` arguments have type annotations.
### Why is this bad?
Type annotations are a good way to document the types of function arguments. They also
help catch bugs, when used alongside a type checker, by ensuring that the types of
any provided arguments match expectation.
Note that many type checkers will infer the type of `self` automatically, so this
annotation is not strictly necessary.
### Example
```python
class Foo:
def bar(self):
...
```
Use instead:
```python
class Foo:
def bar(self: "Foo"):
...
```

View file

@ -0,0 +1,24 @@
# unsorted-imports (I001)
Derived from the **isort** linter.
Autofix is always available.
### What it does
De-duplicates, groups, and sorts imports based on the provided `isort` settings.
### Why is this bad?
Consistency is good. Use a common convention for imports to make your code
more readable and idiomatic.
### Example
```python
import pandas
import numpy as np
```
Use instead:
```python
import numpy as np
import pandas
```