[CVAT] [Recording Oracle] Add ruff#2396
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 2 Skipped Deployments
|
## What it does
Checks for unused imports.
## Why is this bad?
Unused imports add a performance overhead at runtime, and risk creating
import cycles. They also increase the cognitive load of reading the code.
If an import statement is used to check for the availability or existence
of a module, consider using `importlib.util.find_spec` instead.
If an import statement is used to re-export a symbol as part of a module's
public interface, consider using a "redundant" import alias, which
instructs Ruff (and other tools) to respect the re-export, and avoid
marking it as unused, as in:
```python
from module import member as member
```
Alternatively, you can use `__all__` to declare a symbol as part of the module's
interface, as in:
```python
# __init__.py
import some_module
__all__ = ["some_module"]
```
## Fix safety
Fixes to remove unused imports are safe, except in `__init__.py` files.
Applying fixes to `__init__.py` files is currently in preview. The fix offered depends on the
type of the unused import. Ruff will suggest a safe fix to export first-party imports with
either a redundant alias or, if already present in the file, an `__all__` entry. If multiple
`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix
to remove third-party and standard library imports -- the fix is unsafe because the module's
interface changes.
## Example
```python
import numpy as np # unused import
def area(radius):
return 3.14 * radius**2
```
Use instead:
```python
def area(radius):
return 3.14 * radius**2
```
To check the availability of a module, use `importlib.util.find_spec`:
```python
from importlib.util import find_spec
if find_spec("numpy") is not None:
print("numpy is installed")
else:
print("numpy is not installed")
```
## Options
- `lint.ignore-init-module-imports`
## References
- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)
- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)
- [Typing documentation: interface conventions](https://typing.readthedocs.io/en/latest/source/libraries.html#library-interface-public-and-private-symbols)
## What it does
Checks for unused imports.
## Why is this bad?
Unused imports add a performance overhead at runtime, and risk creating
import cycles. They also increase the cognitive load of reading the code.
If an import statement is used to check for the availability or existence
of a module, consider using `importlib.util.find_spec` instead.
If an import statement is used to re-export a symbol as part of a module's
public interface, consider using a "redundant" import alias, which
instructs Ruff (and other tools) to respect the re-export, and avoid
marking it as unused, as in:
```python
from module import member as member
```
Alternatively, you can use `__all__` to declare a symbol as part of the module's
interface, as in:
```python
# __init__.py
import some_module
__all__ = ["some_module"]
```
## Fix safety
Fixes to remove unused imports are safe, except in `__init__.py` files.
Applying fixes to `__init__.py` files is currently in preview. The fix offered depends on the
type of the unused import. Ruff will suggest a safe fix to export first-party imports with
either a redundant alias or, if already present in the file, an `__all__` entry. If multiple
`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix
to remove third-party and standard library imports -- the fix is unsafe because the module's
interface changes.
## Example
```python
import numpy as np # unused import
def area(radius):
return 3.14 * radius**2
```
Use instead:
```python
def area(radius):
return 3.14 * radius**2
```
To check the availability of a module, use `importlib.util.find_spec`:
```python
from importlib.util import find_spec
if find_spec("numpy") is not None:
print("numpy is installed")
else:
print("numpy is not installed")
```
## Options
- `lint.ignore-init-module-imports`
## References
- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)
- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)
- [Typing documentation: interface conventions](https://typing.readthedocs.io/en/latest/source/libraries.html#library-interface-public-and-private-symbols)
## What it does Checks for `zip` calls without an explicit `strict` parameter. ## Why is this bad? By default, if the iterables passed to `zip` are of different lengths, the resulting iterator will be silently truncated to the length of the shortest iterable. This can lead to subtle bugs. Use the `strict` parameter to raise a `ValueError` if the iterables are of non-uniform length. If the iterables are intentionally different lengths, the parameter should be explicitly set to `False`. ## Example ```python zip(a, b) ``` Use instead: ```python zip(a, b, strict=True) ``` ## Fix safety This rule's fix is marked as unsafe for `zip` calls that contain `**kwargs`, as adding a `strict` keyword argument to such a call may lead to a duplicate keyword argument error. ## References - [Python documentation: `zip`](https://docs.python.org/3/library/functions.html#zip)
## What it does
Checks for membership tests using `not {element} in {collection}`.
## Why is this bad?
Testing membership with `{element} not in {collection}` is more readable.
## Example
```python
Z = not X in Y
if not X.B in Y:
pass
```
Use instead:
```python
Z = X not in Y
if X.B not in Y:
pass
```
## What it does
Checks for f-strings that do not contain any placeholder expressions.
## Why is this bad?
f-strings are a convenient way to format strings, but they are not
necessary if there are no placeholder expressions to format. In this
case, a regular string should be used instead, as an f-string without
placeholders can be confusing for readers, who may expect such a
placeholder to be present.
An f-string without any placeholders could also indicate that the
author forgot to add a placeholder expression.
## Example
```python
f"Hello, world!"
```
Use instead:
```python
"Hello, world!"
```
**Note:** to maintain compatibility with PyFlakes, this rule only flags
f-strings that are part of an implicit concatenation if _none_ of the
f-string segments contain placeholder expressions.
For example:
```python
# Will not be flagged.
(
f"Hello,"
f" {name}!"
)
# Will be flagged.
(
f"Hello,"
f" World!"
)
```
See [#10885](astral-sh/ruff#10885) for more.
## References
- [PEP 498](https://www.python.org/dev/peps/pep-0498/)
## What it does Checks for submodule imports that are aliased to the submodule name. ## Why is this bad? Using the `from` keyword to import the submodule is more concise and readable. ## Example ```python import concurrent.futures as futures ``` Use instead: ```python from concurrent import futures ``` ## References - [Python documentation: Submodules](https://docs.python.org/3/reference/import.html#submodules)
## What it does
Checks for conditions that position a constant on the left-hand side of the
comparison operator, rather than the right-hand side.
## Why is this bad?
These conditions (sometimes referred to as "Yoda conditions") are less
readable than conditions that place the variable on the left-hand side of
the comparison operator.
In some languages, Yoda conditions are used to prevent accidental
assignment in conditions (i.e., accidental uses of the `=` operator,
instead of the `==` operator). However, Python does not allow assignments
in conditions unless using the `:=` operator, so Yoda conditions provide
no benefit in this regard.
## Example
```python
if "Foo" == foo:
...
```
Use instead:
```python
if foo == "Foo":
...
```
## References
- [Python documentation: Comparisons](https://docs.python.org/3/reference/expressions.html#comparisons)
- [Python documentation: Assignment statements](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)
## What it does
Checks for `dict.get()` calls that pass `None` as the default value.
## Why is this bad?
`None` is the default value for `dict.get()`, so it is redundant to pass it
explicitly.
## Example
```python
ages = {"Tom": 23, "Maria": 23, "Dog": 11}
age = ages.get("Cat", None)
```
Use instead:
```python
ages = {"Tom": 23, "Maria": 23, "Dog": 11}
age = ages.get("Cat")
```
## References
- [Python documentation: `dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get)
## What it does
Checks for uses of deprecated methods from the `unittest` module.
## Why is this bad?
The `unittest` module has deprecated aliases for some of its methods.
The aliases may be removed in future versions of Python. Instead,
use their non-deprecated counterparts.
## Example
```python
from unittest import TestCase
class SomeTest(TestCase):
def test_something(self):
self.assertEquals(1, 1)
```
Use instead:
```python
from unittest import TestCase
class SomeTest(TestCase):
def test_something(self):
self.assertEqual(1, 1)
```
## References
- [Python documentation: Deprecated aliases](https://docs.python.org/3/library/unittest.html#deprecated-aliases)
## What it does Checks for the use of generics that can be replaced with standard library variants based on [PEP 585]. ## Why is this bad? [PEP 585] enabled collections in the Python standard library (like `list`) to be used as generics directly, instead of importing analogous members from the `typing` module (like `typing.List`). When available, the [PEP 585] syntax should be used instead of importing members from the `typing` module, as it's more concise and readable. Importing those members from `typing` is considered deprecated as of [PEP 585]. This rule is enabled when targeting Python 3.9 or later (see: [`target-version`]). By default, it's _also_ enabled for earlier Python versions if `from __future__ import annotations` is present, as `__future__` annotations are not evaluated at runtime. If your code relies on runtime type annotations (either directly or via a library like Pydantic), you can disable this behavior for Python versions prior to 3.9 by setting [`lint.pyupgrade.keep-runtime-typing`] to `true`. ## Example ```python from typing import List foo: List[int] = [1, 2, 3] ``` Use instead: ```python foo: list[int] = [1, 2, 3] ``` ## Fix safety This rule's fix is marked as unsafe, as it may lead to runtime errors when alongside libraries that rely on runtime type annotations, like Pydantic, on Python versions prior to Python 3.9. ## Options - `target-version` - `lint.pyupgrade.keep-runtime-typing` [PEP 585]: https://peps.python.org/pep-0585/
## What it does Check for type annotations that can be rewritten based on [PEP 604] syntax. ## Why is this bad? [PEP 604] introduced a new syntax for union type annotations based on the `|` operator. This syntax is more concise and readable than the previous `typing.Union` and `typing.Optional` syntaxes. This rule is enabled when targeting Python 3.10 or later (see: [`target-version`]). By default, it's _also_ enabled for earlier Python versions if `from __future__ import annotations` is present, as `__future__` annotations are not evaluated at runtime. If your code relies on runtime type annotations (either directly or via a library like Pydantic), you can disable this behavior for Python versions prior to 3.10 by setting [`lint.pyupgrade.keep-runtime-typing`] to `true`. ## Example ```python from typing import Union foo: Union[int, str] = 1 ``` Use instead: ```python foo: int | str = 1 ``` ## Fix safety This rule's fix is marked as unsafe, as it may lead to runtime errors when alongside libraries that rely on runtime type annotations, like Pydantic, on Python versions prior to Python 3.10. It may also lead to runtime errors in unusual and likely incorrect type annotations where the type does not support the `|` operator. ## Options - `target-version` - `lint.pyupgrade.keep-runtime-typing` [PEP 604]: https://peps.python.org/pep-0604/
## What it does
Checks for unnecessary calls to `encode` as UTF-8.
## Why is this bad?
UTF-8 is the default encoding in Python, so there is no need to call
`encode` when UTF-8 is the desired encoding. Instead, use a bytes literal.
## Example
```python
"foo".encode("utf-8")
```
Use instead:
```python
b"foo"
```
## References
- [Python documentation: `str.encode`](https://docs.python.org/3/library/stdtypes.html#str.encode)
## What it does
Checks for `str.format` calls that can be replaced with f-strings.
## Why is this bad?
f-strings are more readable and generally preferred over `str.format`
calls.
## Example
```python
"{}".format(foo)
```
Use instead:
```python
f"{foo}"
```
## References
- [Python documentation: f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings)
## What it does Checks for uses of deprecated imports based on the minimum supported Python version. ## Why is this bad? Deprecated imports may be removed in future versions of Python, and should be replaced with their new equivalents. Note that, in some cases, it may be preferable to continue importing members from `typing_extensions` even after they're added to the Python standard library, as `typing_extensions` can backport bugfixes and optimizations from later Python versions. This rule thus avoids flagging imports from `typing_extensions` in such cases. ## Example ```python from collections import Sequence ``` Use instead: ```python from collections.abc import Sequence ```
## What it does
Checks for the presence of unnecessary quotes in type annotations.
## Why is this bad?
In Python, type annotations can be quoted to avoid forward references.
However, if `from __future__ import annotations` is present, Python
will always evaluate type annotations in a deferred manner, making
the quotes unnecessary.
Similarly, if the annotation is located in a typing-only context and
won't be evaluated by Python at runtime, the quotes will also be
considered unnecessary. For example, Python does not evaluate type
annotations on assignments in function bodies.
## Example
Given:
```python
from __future__ import annotations
def foo(bar: "Bar") -> "Bar":
...
```
Use instead:
```python
from __future__ import annotations
def foo(bar: Bar) -> Bar:
...
```
Given:
```python
def foo() -> None:
bar: "Bar"
```
Use instead:
```python
def foo() -> None:
bar: Bar
```
## References
- [PEP 563](https://peps.python.org/pep-0563/)
- [Python documentation: `__future__`](https://docs.python.org/3/library/__future__.html#module-__future__)
…-method
## 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.lint.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
```
…ethod
## 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
```
## What it does
Checks for uses of mutable objects as function argument defaults.
## Why is this bad?
Function defaults are evaluated once, when the function is defined.
The same mutable object is then shared across all calls to the function.
If the object is modified, those modifications will persist across calls,
which can lead to unexpected behavior.
Instead, prefer to use immutable data structures, or take `None` as a
default, and initialize a new mutable object inside the function body
for each call.
Arguments with immutable type annotations will be ignored by this rule.
Types outside of the standard library can be marked as immutable with the
[`lint.flake8-bugbear.extend-immutable-calls`] configuration option.
## Known problems
Mutable argument defaults can be used intentionally to cache computation
results. Replacing the default with `None` or an immutable data structure
does not work for such usages. Instead, prefer the `@functools.lru_cache`
decorator from the standard library.
## Example
```python
def add_to_list(item, some_list=[]):
some_list.append(item)
return some_list
l1 = add_to_list(0) # [0]
l2 = add_to_list(1) # [0, 1]
```
Use instead:
```python
def add_to_list(item, some_list=None):
if some_list is None:
some_list = []
some_list.append(item)
return some_list
l1 = add_to_list(0) # [0]
l2 = add_to_list(1) # [1]
```
## Options
- `lint.flake8-bugbear.extend-immutable-calls`
## References
- [Python documentation: Default Argument Values](https://docs.python.org/3/tutorial/controlflow.html#default-argument-values)
## What it does
Checks for unused variables in loops (e.g., `for` and `while` statements).
## Why is this bad?
Defining a variable in a loop statement that is never used can confuse
readers.
If the variable is intended to be unused (e.g., to facilitate
destructuring of a tuple or other object), prefix it with an underscore
to indicate the intent. Otherwise, remove the variable entirely.
## Example
```python
for i, j in foo:
bar(i)
```
Use instead:
```python
for i, _j in foo:
bar(i)
```
## References
- [PEP 8: Naming Conventions](https://peps.python.org/pep-0008/#naming-conventions)
## What it does Checks for uses of `assert False`. ## Why is this bad? Python removes `assert` statements when running in optimized mode (`python -O`), making `assert False` an unreliable means of raising an `AssertionError`. Instead, raise an `AssertionError` directly. ## Example ```python assert False ``` Use instead: ```python raise AssertionError ``` ## Fix safety This rule's fix is marked as unsafe, as changing an `assert` to a `raise` will change the behavior of your program when running in optimized mode (`python -O`). ## References - [Python documentation: `assert`](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement)
## What it does Checks for unnecessary generators that can be rewritten as `list` comprehensions (or with `list` directly). ## Why is this bad? It is unnecessary to use `list` around a generator expression, since there are equivalent comprehensions for these types. Using a comprehension is clearer and more idiomatic. Further, if the comprehension can be removed entirely, as in the case of `list(x for x in foo)`, it's better to use `list(foo)` directly, since it's even more direct. ## Examples ```python list(f(x) for x in foo) list(x for x in foo) list((x for x in foo)) ``` Use instead: ```python [f(x) for x in foo] list(foo) list(foo) ``` ## Fix safety This rule's fix is marked as unsafe, as it may occasionally drop comments when rewriting the call. In most cases, though, comments will be preserved.
## What it does
Checks for unnecessary `dict`, `list` or `tuple` calls that can be
rewritten as empty literals.
## Why is this bad?
It's unnecessary to call, e.g., `dict()` as opposed to using an empty
literal (`{}`). The former is slower because the name `dict` must be
looked up in the global scope in case it has been rebound.
## Examples
```python
dict()
dict(a=1, b=2)
list()
tuple()
```
Use instead:
```python
{}
{"a": 1, "b": 2}
[]
()
```
## Fix safety
This rule's fix is marked as unsafe, as it may occasionally drop comments
when rewriting the call. In most cases, though, comments will be preserved.
## Options
- `lint.flake8-comprehensions.allow-dict-calls-with-keyword-arguments`
## What it does Checks for the presence of unused variables in function scopes. ## Why is this bad? A variable that is defined but not used is likely a mistake, and should be removed to avoid confusion. If a variable is intentionally defined-but-not-used, it should be prefixed with an underscore, or some other value that adheres to the [`lint.dummy-variable-rgx`] pattern. Under [preview mode](https://docs.astral.sh/ruff/preview), this rule also triggers on unused unpacked assignments (for example, `x, y = foo()`). ## Example ```python def foo(): x = 1 y = 2 return x ``` Use instead: ```python def foo(): x = 1 return x ``` ## Options - `lint.dummy-variable-rgx`
## What it does
Checks for `str.join` calls that can be replaced with f-strings.
## Why is this bad?
f-strings are more readable and generally preferred over `str.join` calls.
## Example
```python
" ".join((foo, bar))
```
Use instead:
```python
f"{foo} {bar}"
```
## References
- [Python documentation: f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings)
## What it does
Checks for uses of assertion methods from the `unittest` module.
## Why is this bad?
To make use of `pytest`'s assertion rewriting, a regular `assert` statement
is preferred over `unittest`'s assertion methods.
## Example
```python
import unittest
class TestFoo(unittest.TestCase):
def test_foo(self):
self.assertEqual(a, b)
```
Use instead:
```python
import unittest
class TestFoo(unittest.TestCase):
def test_foo(self):
assert a == b
```
## References
- [`pytest` documentation: Assertion introspection details](https://docs.pytest.org/en/7.1.x/how-to/assert.html#assertion-introspection-details)
…tion
## What it does
Checks for uses of exception-related assertion methods from the `unittest`
module.
## Why is this bad?
To enforce the assertion style recommended by `pytest`, `pytest.raises` is
preferred over the exception-related assertion methods in `unittest`, like
`assertRaises`.
## Example
```python
import unittest
class TestFoo(unittest.TestCase):
def test_foo(self):
with self.assertRaises(ValueError):
raise ValueError("foo")
```
Use instead:
```python
import unittest
import pytest
class TestFoo(unittest.TestCase):
def test_foo(self):
with pytest.raises(ValueError):
raise ValueError("foo")
```
## References
- [`pytest` documentation: Assertions about expected exceptions](https://docs.pytest.org/en/latest/how-to/assert.html#assertions-about-expected-exceptions)
## What it does
Checks for variable assignments that immediately precede a `return` of the
assigned variable.
## Why is this bad?
The variable assignment is not necessary, as the value can be returned
directly.
## Example
```python
def foo():
bar = 1
return bar
```
Use instead:
```python
def foo():
return 1
```
…exception ## What it does Checks for unnecessary parentheses on raised exceptions. ## Why is this bad? If an exception is raised without any arguments, parentheses are not required, as the `raise` statement accepts either an exception instance or an exception class (which is then implicitly instantiated). Removing the parentheses makes the code more concise. ## Known problems Parentheses can only be omitted if the exception is a class, as opposed to a function call. This rule isn't always capable of distinguishing between the two. For example, if you import a function `module.get_exception` from another module, and `module.get_exception` returns an exception object, this rule will incorrectly mark the parentheses in `raise module.get_exception()` as unnecessary. ## Example ```python raise TypeError() ``` Use instead: ```python raise TypeError ``` ## References - [Python documentation: The `raise` statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement)
## What it does
Checks for multiple `isinstance` calls on the same target.
## Why is this bad?
To check if an object is an instance of any one of multiple types
or classes, it is unnecessary to use multiple `isinstance` calls, as
the second argument of the `isinstance` built-in function accepts a
tuple of types and classes.
Using a single `isinstance` call implements the same behavior with more
concise code and clearer intent.
## Example
```python
if isinstance(obj, int) or isinstance(obj, float):
pass
```
Use instead:
```python
if isinstance(obj, (int, float)):
pass
```
## References
- [Python documentation: `isinstance`](https://docs.python.org/3/library/functions.html#isinstance)
## What it does
Checks for nested `if` statements that can be collapsed into a single `if`
statement.
## Why is this bad?
Nesting `if` statements leads to deeper indentation and makes code harder to
read. Instead, combine the conditions into a single `if` statement with an
`and` operator.
## Example
```python
if foo:
if bar:
...
```
Use instead:
```python
if foo and bar:
...
```
## References
- [Python documentation: The `if` statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)
- [Python documentation: Boolean operations](https://docs.python.org/3/reference/expressions.html#boolean-operations)
…-exp
## What it does
Check for `if`-`else`-blocks that can be replaced with a ternary operator.
## Why is this bad?
`if`-`else`-blocks that assign a value to a variable in both branches can
be expressed more concisely by using a ternary operator.
## Example
```python
if foo:
bar = x
else:
bar = y
```
Use instead:
```python
bar = x if foo else y
```
## References
- [Python documentation: Conditional expressions](https://docs.python.org/3/reference/expressions.html#conditional-expressions)
## What it does
Checks for first-party imports that are only used for type annotations, but
aren't defined in a type-checking block.
## Why is this bad?
Unused imports add a performance overhead at runtime, and risk creating
import cycles. If an import is _only_ used in typing-only contexts, it can
instead be imported conditionally under an `if TYPE_CHECKING:` block to
minimize runtime overhead.
If [`lint.flake8-type-checking.quote-annotations`] is set to `true`,
annotations will be wrapped in quotes if doing so would enable the
corresponding import to be moved into an `if TYPE_CHECKING:` block.
If a class _requires_ that type annotations be available at runtime (as is
the case for Pydantic, SQLAlchemy, and other libraries), consider using
the [`lint.flake8-type-checking.runtime-evaluated-base-classes`] and
[`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
as such.
## Example
```python
from __future__ import annotations
import local_module
def func(sized: local_module.Container) -> int:
return len(sized)
```
Use instead:
```python
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import local_module
def func(sized: local_module.Container) -> int:
return len(sized)
```
## Options
- `lint.flake8-type-checking.quote-annotations`
- `lint.flake8-type-checking.runtime-evaluated-base-classes`
- `lint.flake8-type-checking.runtime-evaluated-decorators`
- `lint.typing-modules`
## References
- [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)
## What it does
Checks for third-party imports that are only used for type annotations, but
aren't defined in a type-checking block.
## Why is this bad?
Unused imports add a performance overhead at runtime, and risk creating
import cycles. If an import is _only_ used in typing-only contexts, it can
instead be imported conditionally under an `if TYPE_CHECKING:` block to
minimize runtime overhead.
If [`lint.flake8-type-checking.quote-annotations`] is set to `true`,
annotations will be wrapped in quotes if doing so would enable the
corresponding import to be moved into an `if TYPE_CHECKING:` block.
If a class _requires_ that type annotations be available at runtime (as is
the case for Pydantic, SQLAlchemy, and other libraries), consider using
the [`lint.flake8-type-checking.runtime-evaluated-base-classes`] and
[`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
as such.
## Example
```python
from __future__ import annotations
import pandas as pd
def func(df: pd.DataFrame) -> int:
return len(df)
```
Use instead:
```python
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import pandas as pd
def func(df: pd.DataFrame) -> int:
return len(df)
```
## Options
- `lint.flake8-type-checking.quote-annotations`
- `lint.flake8-type-checking.runtime-evaluated-base-classes`
- `lint.flake8-type-checking.runtime-evaluated-decorators`
- `lint.typing-modules`
## References
- [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)
…y-import
## What it does
Checks for standard library imports that are only used for type
annotations, but aren't defined in a type-checking block.
## Why is this bad?
Unused imports add a performance overhead at runtime, and risk creating
import cycles. If an import is _only_ used in typing-only contexts, it can
instead be imported conditionally under an `if TYPE_CHECKING:` block to
minimize runtime overhead.
If [`lint.flake8-type-checking.quote-annotations`] is set to `true`,
annotations will be wrapped in quotes if doing so would enable the
corresponding import to be moved into an `if TYPE_CHECKING:` block.
If a class _requires_ that type annotations be available at runtime (as is
the case for Pydantic, SQLAlchemy, and other libraries), consider using
the [`lint.flake8-type-checking.runtime-evaluated-base-classes`] and
[`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
as such.
## Example
```python
from __future__ import annotations
from pathlib import Path
def func(path: Path) -> str:
return str(path)
```
Use instead:
```python
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
def func(path: Path) -> str:
return str(path)
```
## Options
- `lint.flake8-type-checking.quote-annotations`
- `lint.flake8-type-checking.runtime-evaluated-base-classes`
- `lint.flake8-type-checking.runtime-evaluated-decorators`
- `lint.typing-modules`
## References
- [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)
## What it does
Checks for conditions that position a constant on the left-hand side of the
comparison operator, rather than the right-hand side.
## Why is this bad?
These conditions (sometimes referred to as "Yoda conditions") are less
readable than conditions that place the variable on the left-hand side of
the comparison operator.
In some languages, Yoda conditions are used to prevent accidental
assignment in conditions (i.e., accidental uses of the `=` operator,
instead of the `==` operator). However, Python does not allow assignments
in conditions unless using the `:=` operator, so Yoda conditions provide
no benefit in this regard.
## Example
```python
if "Foo" == foo:
...
```
Use instead:
```python
if foo == "Foo":
...
```
## References
- [Python documentation: Comparisons](https://docs.python.org/3/reference/expressions.html#comparisons)
- [Python documentation: Assignment statements](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)
## What it does
Checks for the use of implicit `Optional` in type annotations when the
default parameter value is `None`.
## Why is this bad?
Implicit `Optional` is prohibited by [PEP 484]. It is confusing and
inconsistent with the rest of the type system.
It's recommended to use `Optional[T]` instead. For Python 3.10 and later,
you can also use `T | None`.
## Example
```python
def foo(arg: int = None):
pass
```
Use instead:
```python
from typing import Optional
def foo(arg: Optional[int] = None):
pass
```
Or, for Python 3.10 and later:
```python
def foo(arg: int | None = None):
pass
```
If you want to use the `|` operator in Python 3.9 and earlier, you can
use future imports:
```python
from __future__ import annotations
def foo(arg: int | None = None):
pass
```
## Limitations
Type aliases are not supported and could result in false negatives.
For example, the following code will not be flagged:
```python
Text = str | bytes
def foo(arg: Text = None):
pass
```
## Options
- `target-version`
[PEP 484]: https://peps.python.org/pep-0484/#union-types
## What it does Checks for uses of `isinstance` and `issubclass` that take a tuple of types for comparison. ## Why is this bad? Since Python 3.10, `isinstance` and `issubclass` can be passed a `|`-separated union of types, which is consistent with the union operator introduced in [PEP 604]. Note that this results in slower code. Ignore this rule if the performance of an `isinstance` or `issubclass` check is a concern, e.g., in a hot loop. ## Example ```python isinstance(x, (int, float)) ``` Use instead: ```python isinstance(x, int | float) ``` ## Options - `target-version` ## References - [Python documentation: `isinstance`](https://docs.python.org/3/library/functions.html#isinstance) - [Python documentation: `issubclass`](https://docs.python.org/3/library/functions.html#issubclass) [PEP 604]: https://peps.python.org/pep-0604/
## What it does Checks for relative imports. ## Why is this bad? Absolute imports, or relative imports from siblings, are recommended by [PEP 8]: > Absolute imports are recommended, as they are usually more readable and tend to be better behaved... > ```python > import mypkg.sibling > from mypkg import sibling > from mypkg.sibling import example > ``` > However, explicit relative imports are an acceptable alternative to absolute imports, > especially when dealing with complex package layouts where using absolute imports would be > unnecessarily verbose: > ```python > from . import sibling > from .sibling import example > ``` ## Example ```python from .. import foo ``` Use instead: ```python from mypkg import foo ``` ## Options - `lint.flake8-tidy-imports.ban-relative-imports` [PEP 8]: https://peps.python.org/pep-0008/#imports
01e308c to
e4b92ee
Compare
e4b92ee to
6edcfaf
Compare
6edcfaf to
5514647
Compare
zhiltsov-max
approved these changes
Aug 27, 2024
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds ruff and integrates linting into the CI pipeline.
Ruff combines features from
flake8(with plugins),pylint,isort,black,autopep8,flynt, etc., into one extremely fast and well-maintained package.One of the main features is availability of automatic fixes for some violations.
Relying on a tool like Ruff helps to reduce cognitive load when writing/reading the code by enforcing consistency.
Summary of changes
I've adopted the following strategy for integrating Ruff:
# noqafor remaining violations where necessary.Each commit with autofix has a comment explaining the reasoning behind the changes.
How to test the changes
The code should be semantically equivalent, with only stylistic and formatting changes. I've ensured that all tests pass.
References
After this PR and #2395 will get merged, I'll introduce a PR adding
.git-blame-ignore-revswith revisions from merge commits, so these changes will be ignored by git blame for better history overview.