mirror of https://github.com/astral-sh/ruff
Improve docs on how to stop Ruff and ty disagreeing with each other (#21644)
## Summary Lots of Ruff rules encourage you to make changes that might then cause ty to start complaining about Liskov violations. Most of these Ruff rules already refrain from complaining about a method if they see that the method is decorated with `@override`, but this usually isn't documented. This PR updates the docs of many Ruff rules to note that they refrain from complaining about `@override`-decorated methods, and also adds a similar note to the ty `invalid-method-override` documentation. Helps with https://github.com/astral-sh/ty/issues/1644#issuecomment-3581663859 ## Test Plan - `uvx prek run -a` locally - CI on this PR
This commit is contained in:
parent
c7107a5a90
commit
792ec3e96e
|
|
@ -25,6 +25,11 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||||
/// keyword-only argument, to force callers to be explicit when providing
|
/// keyword-only argument, to force callers to be explicit when providing
|
||||||
/// the argument.
|
/// the argument.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override],
|
||||||
|
/// since changing the signature of a subclass method that overrides a
|
||||||
|
/// superclass method may cause type checkers to complain about a violation of
|
||||||
|
/// the Liskov Substitution Principle.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// from math import ceil, floor
|
/// from math import ceil, floor
|
||||||
|
|
@ -89,6 +94,8 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||||
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.127")]
|
#[violation_metadata(stable_since = "v0.0.127")]
|
||||||
pub(crate) struct BooleanDefaultValuePositionalArgument;
|
pub(crate) struct BooleanDefaultValuePositionalArgument;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||||
/// the argument.
|
/// the argument.
|
||||||
///
|
///
|
||||||
/// Dunder methods that define operators are exempt from this rule, as are
|
/// Dunder methods that define operators are exempt from this rule, as are
|
||||||
/// setters and `@override` definitions.
|
/// setters and [`@override`][override] definitions.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
|
|
@ -93,6 +93,8 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||||
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.127")]
|
#[violation_metadata(stable_since = "v0.0.127")]
|
||||||
pub(crate) struct BooleanTypeHintPositionalArgument;
|
pub(crate) struct BooleanTypeHintPositionalArgument;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
||||||
/// non-obvious errors, as readers may mistake the argument for the
|
/// non-obvious errors, as readers may mistake the argument for the
|
||||||
/// builtin and vice versa.
|
/// builtin and vice versa.
|
||||||
///
|
///
|
||||||
|
/// Function definitions decorated with [`@override`][override] or
|
||||||
|
/// [`@overload`][overload] are exempt from this rule by default.
|
||||||
/// Builtins can be marked as exceptions to this rule via the
|
/// Builtins can be marked as exceptions to this rule via the
|
||||||
/// [`lint.flake8-builtins.ignorelist`] configuration option.
|
/// [`lint.flake8-builtins.ignorelist`] configuration option.
|
||||||
///
|
///
|
||||||
|
|
@ -48,6 +50,9 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
||||||
/// ## References
|
/// ## References
|
||||||
/// - [_Is it bad practice to use a built-in function name as an attribute or method identifier?_](https://stackoverflow.com/questions/9109333/is-it-bad-practice-to-use-a-built-in-function-name-as-an-attribute-or-method-ide)
|
/// - [_Is it bad practice to use a built-in function name as an attribute or method identifier?_](https://stackoverflow.com/questions/9109333/is-it-bad-practice-to-use-a-built-in-function-name-as-an-attribute-or-method-ide)
|
||||||
/// - [_Why is it a bad idea to name a variable `id` in Python?_](https://stackoverflow.com/questions/77552/id-is-a-bad-variable-name-in-python)
|
/// - [_Why is it a bad idea to name a variable `id` in Python?_](https://stackoverflow.com/questions/77552/id-is-a-bad-variable-name-in-python)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
|
/// [overload]: https://docs.python.org/3/library/typing.html#typing.overload
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.48")]
|
#[violation_metadata(stable_since = "v0.0.48")]
|
||||||
pub(crate) struct BuiltinArgumentShadowing {
|
pub(crate) struct BuiltinArgumentShadowing {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,16 @@ impl Violation for UnusedFunctionArgument {
|
||||||
/// prefixed with an underscore, or some other value that adheres to the
|
/// prefixed with an underscore, or some other value that adheres to the
|
||||||
/// [`lint.dummy-variable-rgx`] pattern.
|
/// [`lint.dummy-variable-rgx`] pattern.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Removing a parameter from a subclass method (or changing a parameter's
|
||||||
|
/// name) may cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused arguments on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Class:
|
/// class Class:
|
||||||
|
|
@ -76,6 +86,8 @@ impl Violation for UnusedFunctionArgument {
|
||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.dummy-variable-rgx`
|
/// - `lint.dummy-variable-rgx`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.168")]
|
#[violation_metadata(stable_since = "v0.0.168")]
|
||||||
pub(crate) struct UnusedMethodArgument {
|
pub(crate) struct UnusedMethodArgument {
|
||||||
|
|
@ -101,6 +113,16 @@ impl Violation for UnusedMethodArgument {
|
||||||
/// prefixed with an underscore, or some other value that adheres to the
|
/// prefixed with an underscore, or some other value that adheres to the
|
||||||
/// [`lint.dummy-variable-rgx`] pattern.
|
/// [`lint.dummy-variable-rgx`] pattern.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Removing a parameter from a subclass method (or changing a parameter's
|
||||||
|
/// name) may cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused arguments on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Class:
|
/// class Class:
|
||||||
|
|
@ -119,6 +141,8 @@ impl Violation for UnusedMethodArgument {
|
||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.dummy-variable-rgx`
|
/// - `lint.dummy-variable-rgx`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.168")]
|
#[violation_metadata(stable_since = "v0.0.168")]
|
||||||
pub(crate) struct UnusedClassMethodArgument {
|
pub(crate) struct UnusedClassMethodArgument {
|
||||||
|
|
@ -144,6 +168,16 @@ impl Violation for UnusedClassMethodArgument {
|
||||||
/// prefixed with an underscore, or some other value that adheres to the
|
/// prefixed with an underscore, or some other value that adheres to the
|
||||||
/// [`lint.dummy-variable-rgx`] pattern.
|
/// [`lint.dummy-variable-rgx`] pattern.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Removing a parameter from a subclass method (or changing a parameter's
|
||||||
|
/// name) may cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method, and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused arguments on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Class:
|
/// class Class:
|
||||||
|
|
@ -162,6 +196,8 @@ impl Violation for UnusedClassMethodArgument {
|
||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.dummy-variable-rgx`
|
/// - `lint.dummy-variable-rgx`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.168")]
|
#[violation_metadata(stable_since = "v0.0.168")]
|
||||||
pub(crate) struct UnusedStaticMethodArgument {
|
pub(crate) struct UnusedStaticMethodArgument {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use crate::checkers::ast::Checker;
|
||||||
/// > mixedCase is allowed only in contexts where that’s already the
|
/// > mixedCase is allowed only in contexts where that’s already the
|
||||||
/// > prevailing style (e.g. threading.py), to retain backwards compatibility.
|
/// > prevailing style (e.g. threading.py), to retain backwards compatibility.
|
||||||
///
|
///
|
||||||
/// Methods decorated with `@typing.override` are ignored.
|
/// Methods decorated with [`@typing.override`][override] are ignored.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
|
|
@ -43,6 +43,8 @@ use crate::checkers::ast::Checker;
|
||||||
///
|
///
|
||||||
/// [PEP 8]: https://peps.python.org/pep-0008/#function-and-method-arguments
|
/// [PEP 8]: https://peps.python.org/pep-0008/#function-and-method-arguments
|
||||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.77")]
|
#[violation_metadata(stable_since = "v0.0.77")]
|
||||||
pub(crate) struct InvalidArgumentName {
|
pub(crate) struct InvalidArgumentName {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,11 @@ use crate::rules::pep8_naming::settings::IgnoreNames;
|
||||||
/// to ignore all functions starting with `test_` from this rule, set the
|
/// to ignore all functions starting with `test_` from this rule, set the
|
||||||
/// [`lint.pep8-naming.extend-ignore-names`] option to `["test_*"]`.
|
/// [`lint.pep8-naming.extend-ignore-names`] option to `["test_*"]`.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Explicitly decorating a method with `@override` signals to Ruff that the method is intended
|
||||||
|
/// to override a superclass method, and that a type checker will enforce that it does so. Ruff
|
||||||
|
/// therefore knows that it should not enforce naming conventions on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// def myFunction():
|
/// def myFunction():
|
||||||
|
|
@ -41,6 +46,7 @@ use crate::rules::pep8_naming::settings::IgnoreNames;
|
||||||
/// - `lint.pep8-naming.extend-ignore-names`
|
/// - `lint.pep8-naming.extend-ignore-names`
|
||||||
///
|
///
|
||||||
/// [PEP 8]: https://peps.python.org/pep-0008/#function-and-variable-names
|
/// [PEP 8]: https://peps.python.org/pep-0008/#function-and-variable-names
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.77")]
|
#[violation_metadata(stable_since = "v0.0.77")]
|
||||||
pub(crate) struct InvalidFunctionName {
|
pub(crate) struct InvalidFunctionName {
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,12 @@ impl Violation for UndocumentedPublicClass {
|
||||||
/// If the codebase adheres to a standard format for method docstrings, follow
|
/// If the codebase adheres to a standard format for method docstrings, follow
|
||||||
/// that format for consistency.
|
/// that format for consistency.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override],
|
||||||
|
/// since it is a common practice to document a method on a superclass but not
|
||||||
|
/// on an overriding method in a subclass.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
|
///
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Cat(Animal):
|
/// class Cat(Animal):
|
||||||
/// def greet(self, happy: bool = True):
|
/// def greet(self, happy: bool = True):
|
||||||
|
|
@ -180,6 +185,7 @@ impl Violation for UndocumentedPublicClass {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Use instead (in the NumPy docstring format):
|
/// Use instead (in the NumPy docstring format):
|
||||||
|
///
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Cat(Animal):
|
/// class Cat(Animal):
|
||||||
/// def greet(self, happy: bool = True):
|
/// def greet(self, happy: bool = True):
|
||||||
|
|
@ -202,6 +208,7 @@ impl Violation for UndocumentedPublicClass {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Or (in the Google docstring format):
|
/// Or (in the Google docstring format):
|
||||||
|
///
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Cat(Animal):
|
/// class Cat(Animal):
|
||||||
/// def greet(self, happy: bool = True):
|
/// def greet(self, happy: bool = True):
|
||||||
|
|
@ -227,6 +234,8 @@ impl Violation for UndocumentedPublicClass {
|
||||||
/// - [PEP 287 – reStructuredText Docstring Format](https://peps.python.org/pep-0287/)
|
/// - [PEP 287 – reStructuredText Docstring Format](https://peps.python.org/pep-0287/)
|
||||||
/// - [NumPy Style Guide](https://numpydoc.readthedocs.io/en/latest/format.html)
|
/// - [NumPy Style Guide](https://numpydoc.readthedocs.io/en/latest/format.html)
|
||||||
/// - [Google Python Style Guide - Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
|
/// - [Google Python Style Guide - Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.70")]
|
#[violation_metadata(stable_since = "v0.0.70")]
|
||||||
pub(crate) struct UndocumentedPublicMethod;
|
pub(crate) struct UndocumentedPublicMethod;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use crate::rules::pylint::helpers::is_known_dunder_method;
|
||||||
///
|
///
|
||||||
/// This rule will detect all methods starting and ending with at least
|
/// This rule will detect all methods starting and ending with at least
|
||||||
/// one underscore (e.g., `_str_`), but ignores known dunder methods (like
|
/// one underscore (e.g., `_str_`), but ignores known dunder methods (like
|
||||||
/// `__init__`), as well as methods that are marked with `@override`.
|
/// `__init__`), as well as methods that are marked with [`@override`][override].
|
||||||
///
|
///
|
||||||
/// Additional dunder methods names can be allowed via the
|
/// Additional dunder methods names can be allowed via the
|
||||||
/// [`lint.pylint.allow-dunder-method-names`] setting.
|
/// [`lint.pylint.allow-dunder-method-names`] setting.
|
||||||
|
|
@ -42,6 +42,8 @@ use crate::rules::pylint::helpers::is_known_dunder_method;
|
||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.pylint.allow-dunder-method-names`
|
/// - `lint.pylint.allow-dunder-method-names`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(preview_since = "v0.0.285")]
|
#[violation_metadata(preview_since = "v0.0.285")]
|
||||||
pub(crate) struct BadDunderMethodName {
|
pub(crate) struct BadDunderMethodName {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,16 @@ use crate::rules::flake8_unused_arguments::rules::is_not_implemented_stub_with_v
|
||||||
/// Unused `self` parameters are usually a sign of a method that could be
|
/// Unused `self` parameters are usually a sign of a method that could be
|
||||||
/// replaced by a function, class method, or static method.
|
/// replaced by a function, class method, or static method.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Converting an instance method into a static method or class method may
|
||||||
|
/// cause type checkers to complain about a violation of the Liskov
|
||||||
|
/// Substitution Principle if it means that the method now incompatibly
|
||||||
|
/// overrides a method defined on a superclass. Explicitly decorating an
|
||||||
|
/// overriding method with `@override` signals to Ruff that the method is
|
||||||
|
/// intended to override a superclass method and that a type checker will
|
||||||
|
/// enforce that it does so; Ruff therefore knows that it should not enforce
|
||||||
|
/// rules about unused `self` parameters on such methods.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// class Person:
|
/// class Person:
|
||||||
|
|
@ -38,6 +48,8 @@ use crate::rules::flake8_unused_arguments::rules::is_not_implemented_stub_with_v
|
||||||
/// def greeting():
|
/// def greeting():
|
||||||
/// print("Greetings friend!")
|
/// print("Greetings friend!")
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(preview_since = "v0.0.286")]
|
#[violation_metadata(preview_since = "v0.0.286")]
|
||||||
pub(crate) struct NoSelfUse {
|
pub(crate) struct NoSelfUse {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,16 @@ use crate::checkers::ast::Checker;
|
||||||
/// By default, this rule allows up to five arguments, as configured by the
|
/// By default, this rule allows up to five arguments, as configured by the
|
||||||
/// [`lint.pylint.max-args`] option.
|
/// [`lint.pylint.max-args`] option.
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Changing the signature of a subclass method may cause type checkers to
|
||||||
|
/// complain about a violation of the Liskov Substitution Principle if it
|
||||||
|
/// means that the method now incompatibly overrides a method defined on a
|
||||||
|
/// superclass. Explicitly decorating an overriding method with `@override`
|
||||||
|
/// signals to Ruff that the method is intended to override a superclass
|
||||||
|
/// method and that a type checker will enforce that it does so; Ruff
|
||||||
|
/// therefore knows that it should not enforce rules about methods having
|
||||||
|
/// too many arguments.
|
||||||
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Functions with many arguments are harder to understand, maintain, and call.
|
/// Functions with many arguments are harder to understand, maintain, and call.
|
||||||
/// Consider refactoring functions with many arguments into smaller functions
|
/// Consider refactoring functions with many arguments into smaller functions
|
||||||
|
|
@ -43,6 +53,8 @@ use crate::checkers::ast::Checker;
|
||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.pylint.max-args`
|
/// - `lint.pylint.max-args`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.238")]
|
#[violation_metadata(stable_since = "v0.0.238")]
|
||||||
pub(crate) struct TooManyArguments {
|
pub(crate) struct TooManyArguments {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,16 @@ use crate::checkers::ast::Checker;
|
||||||
/// with fewer arguments, using objects to group related arguments, or migrating to
|
/// with fewer arguments, using objects to group related arguments, or migrating to
|
||||||
/// [keyword-only arguments](https://docs.python.org/3/tutorial/controlflow.html#special-parameters).
|
/// [keyword-only arguments](https://docs.python.org/3/tutorial/controlflow.html#special-parameters).
|
||||||
///
|
///
|
||||||
|
/// This rule exempts methods decorated with [`@typing.override`][override].
|
||||||
|
/// Changing the signature of a subclass method may cause type checkers to
|
||||||
|
/// complain about a violation of the Liskov Substitution Principle if it
|
||||||
|
/// means that the method now incompatibly overrides a method defined on a
|
||||||
|
/// superclass. Explicitly decorating an overriding method with `@override`
|
||||||
|
/// signals to Ruff that the method is intended to override a superclass
|
||||||
|
/// method and that a type checker will enforce that it does so; Ruff
|
||||||
|
/// therefore knows that it should not enforce rules about methods having
|
||||||
|
/// too many arguments.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
/// ```python
|
/// ```python
|
||||||
|
|
@ -41,6 +51,8 @@ use crate::checkers::ast::Checker;
|
||||||
///
|
///
|
||||||
/// ## Options
|
/// ## Options
|
||||||
/// - `lint.pylint.max-positional-args`
|
/// - `lint.pylint.max-positional-args`
|
||||||
|
///
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(preview_since = "v0.1.7")]
|
#[violation_metadata(preview_since = "v0.1.7")]
|
||||||
pub(crate) struct TooManyPositionalArguments {
|
pub(crate) struct TooManyPositionalArguments {
|
||||||
|
|
|
||||||
|
|
@ -1012,7 +1012,28 @@ classes in Python do indeed behave this way, the strongly held convention is tha
|
||||||
be avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__`
|
be avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__`
|
||||||
and `__ne__` methods accept `object` as their second argument.
|
and `__ne__` methods accept `object` as their second argument.
|
||||||
|
|
||||||
|
**Why does ty disagree with Ruff about how to write my method?**
|
||||||
|
|
||||||
|
|
||||||
|
Ruff has several rules that will encourage you to rename a parameter, or change its type
|
||||||
|
signature, if it thinks you're falling into a certain anti-pattern. For example, Ruff's
|
||||||
|
[ARG002](https://docs.astral.sh/ruff/rules/unused-method-argument/) rule recommends that an
|
||||||
|
unused parameter should either be removed or renamed to start with `_`. Applying either of
|
||||||
|
these suggestions can cause ty to start reporting an `invalid-method-override` error if
|
||||||
|
the function in question is a method on a subclass that overrides a method on a superclass,
|
||||||
|
and the change would cause the subclass method to no longer accept all argument combinations
|
||||||
|
that the superclass method accepts.
|
||||||
|
|
||||||
|
This can usually be resolved by adding [`@typing.override`][override] to your method
|
||||||
|
definition. Ruff knows that a method decorated with `@typing.override` is intended to
|
||||||
|
override a method by the same name on a superclass, and avoids reporting rules like ARG002
|
||||||
|
for such methods; it knows that the changes recommended by ARG002 would violate the Liskov
|
||||||
|
Substitution Principle.
|
||||||
|
|
||||||
|
Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
|
||||||
|
|
||||||
[Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle
|
[Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle
|
||||||
|
[override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
|
|
||||||
## `invalid-named-tuple`
|
## `invalid-named-tuple`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2114,7 +2114,27 @@ declare_lint! {
|
||||||
/// be avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__`
|
/// be avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__`
|
||||||
/// and `__ne__` methods accept `object` as their second argument.
|
/// and `__ne__` methods accept `object` as their second argument.
|
||||||
///
|
///
|
||||||
|
/// ### Why does ty disagree with Ruff about how to write my method?
|
||||||
|
///
|
||||||
|
/// Ruff has several rules that will encourage you to rename a parameter, or change its type
|
||||||
|
/// signature, if it thinks you're falling into a certain anti-pattern. For example, Ruff's
|
||||||
|
/// [ARG002](https://docs.astral.sh/ruff/rules/unused-method-argument/) rule recommends that an
|
||||||
|
/// unused parameter should either be removed or renamed to start with `_`. Applying either of
|
||||||
|
/// these suggestions can cause ty to start reporting an `invalid-method-override` error if
|
||||||
|
/// the function in question is a method on a subclass that overrides a method on a superclass,
|
||||||
|
/// and the change would cause the subclass method to no longer accept all argument combinations
|
||||||
|
/// that the superclass method accepts.
|
||||||
|
///
|
||||||
|
/// This can usually be resolved by adding [`@typing.override`][override] to your method
|
||||||
|
/// definition. Ruff knows that a method decorated with `@typing.override` is intended to
|
||||||
|
/// override a method by the same name on a superclass, and avoids reporting rules like ARG002
|
||||||
|
/// for such methods; it knows that the changes recommended by ARG002 would violate the Liskov
|
||||||
|
/// Substitution Principle.
|
||||||
|
///
|
||||||
|
/// Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
|
||||||
|
///
|
||||||
/// [Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle
|
/// [Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle
|
||||||
|
/// [override]: https://docs.python.org/3/library/typing.html#typing.override
|
||||||
pub(crate) static INVALID_METHOD_OVERRIDE = {
|
pub(crate) static INVALID_METHOD_OVERRIDE = {
|
||||||
summary: "detects method definitions that violate the Liskov Substitution Principle",
|
summary: "detects method definitions that violate the Liskov Substitution Principle",
|
||||||
status: LintStatus::stable("0.0.1-alpha.20"),
|
status: LintStatus::stable("0.0.1-alpha.20"),
|
||||||
|
|
|
||||||
|
|
@ -635,7 +635,7 @@
|
||||||
},
|
},
|
||||||
"invalid-method-override": {
|
"invalid-method-override": {
|
||||||
"title": "detects method definitions that violate the Liskov Substitution Principle",
|
"title": "detects method definitions that violate the Liskov Substitution Principle",
|
||||||
"description": "## What it does\nDetects method overrides that violate the [Liskov Substitution Principle] (\"LSP\").\n\nThe LSP states that an instance of a subtype should be substitutable for an instance of its supertype.\nApplied to Python, this means:\n1. All argument combinations a superclass method accepts\n must also be accepted by an overriding subclass method.\n2. The return type of an overriding subclass method must be a subtype\n of the return type of the superclass method.\n\n## Why is this bad?\nViolating the Liskov Substitution Principle will lead to many of ty's assumptions and\ninferences being incorrect, which will mean that it will fail to catch many possible\ntype errors in your code.\n\n## Example\n```python\nclass Super:\n def method(self, x) -> int:\n return 42\n\nclass Sub(Super):\n # Liskov violation: `str` is not a subtype of `int`,\n # but the supertype method promises to return an `int`.\n def method(self, x) -> str: # error: [invalid-override]\n return \"foo\"\n\ndef accepts_super(s: Super) -> int:\n return s.method(x=42)\n\naccepts_super(Sub()) # The result of this call is a string, but ty will infer\n # it to be an `int` due to the violation of the Liskov Substitution Principle.\n\nclass Sub2(Super):\n # Liskov violation: the superclass method can be called with a `x=`\n # keyword argument, but the subclass method does not accept it.\n def method(self, y) -> int: # error: [invalid-override]\n return 42\n\naccepts_super(Sub2()) # TypeError at runtime: method() got an unexpected keyword argument 'x'\n # ty cannot catch this error due to the violation of the Liskov Substitution Principle.\n```\n\n## Common issues\n\n### Why does ty complain about my `__eq__` method?\n\n`__eq__` and `__ne__` methods in Python are generally expected to accept arbitrary\nobjects as their second argument, for example:\n\n```python\nclass A:\n x: int\n\n def __eq__(self, other: object) -> bool:\n # gracefully handle an object of an unexpected type\n # without raising an exception\n if not isinstance(other, A):\n return False\n return self.x == other.x\n```\n\nIf `A.__eq__` here were annotated as only accepting `A` instances for its second argument,\nit would imply that you wouldn't be able to use `==` between instances of `A` and\ninstances of unrelated classes without an exception possibly being raised. While some\nclasses in Python do indeed behave this way, the strongly held convention is that it should\nbe avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__`\nand `__ne__` methods accept `object` as their second argument.\n\n[Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle",
|
"description": "## What it does\nDetects method overrides that violate the [Liskov Substitution Principle] (\"LSP\").\n\nThe LSP states that an instance of a subtype should be substitutable for an instance of its supertype.\nApplied to Python, this means:\n1. All argument combinations a superclass method accepts\n must also be accepted by an overriding subclass method.\n2. The return type of an overriding subclass method must be a subtype\n of the return type of the superclass method.\n\n## Why is this bad?\nViolating the Liskov Substitution Principle will lead to many of ty's assumptions and\ninferences being incorrect, which will mean that it will fail to catch many possible\ntype errors in your code.\n\n## Example\n```python\nclass Super:\n def method(self, x) -> int:\n return 42\n\nclass Sub(Super):\n # Liskov violation: `str` is not a subtype of `int`,\n # but the supertype method promises to return an `int`.\n def method(self, x) -> str: # error: [invalid-override]\n return \"foo\"\n\ndef accepts_super(s: Super) -> int:\n return s.method(x=42)\n\naccepts_super(Sub()) # The result of this call is a string, but ty will infer\n # it to be an `int` due to the violation of the Liskov Substitution Principle.\n\nclass Sub2(Super):\n # Liskov violation: the superclass method can be called with a `x=`\n # keyword argument, but the subclass method does not accept it.\n def method(self, y) -> int: # error: [invalid-override]\n return 42\n\naccepts_super(Sub2()) # TypeError at runtime: method() got an unexpected keyword argument 'x'\n # ty cannot catch this error due to the violation of the Liskov Substitution Principle.\n```\n\n## Common issues\n\n### Why does ty complain about my `__eq__` method?\n\n`__eq__` and `__ne__` methods in Python are generally expected to accept arbitrary\nobjects as their second argument, for example:\n\n```python\nclass A:\n x: int\n\n def __eq__(self, other: object) -> bool:\n # gracefully handle an object of an unexpected type\n # without raising an exception\n if not isinstance(other, A):\n return False\n return self.x == other.x\n```\n\nIf `A.__eq__` here were annotated as only accepting `A` instances for its second argument,\nit would imply that you wouldn't be able to use `==` between instances of `A` and\ninstances of unrelated classes without an exception possibly being raised. While some\nclasses in Python do indeed behave this way, the strongly held convention is that it should\nbe avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__`\nand `__ne__` methods accept `object` as their second argument.\n\n### Why does ty disagree with Ruff about how to write my method?\n\nRuff has several rules that will encourage you to rename a parameter, or change its type\nsignature, if it thinks you're falling into a certain anti-pattern. For example, Ruff's\n[ARG002](https://docs.astral.sh/ruff/rules/unused-method-argument/) rule recommends that an\nunused parameter should either be removed or renamed to start with `_`. Applying either of\nthese suggestions can cause ty to start reporting an `invalid-method-override` error if\nthe function in question is a method on a subclass that overrides a method on a superclass,\nand the change would cause the subclass method to no longer accept all argument combinations\nthat the superclass method accepts.\n\nThis can usually be resolved by adding [`@typing.override`][override] to your method\ndefinition. Ruff knows that a method decorated with `@typing.override` is intended to\noverride a method by the same name on a superclass, and avoids reporting rules like ARG002\nfor such methods; it knows that the changes recommended by ARG002 would violate the Liskov\nSubstitution Principle.\n\nCorrect use of `@override` is enforced by ty's `invalid-explicit-override` rule.\n\n[Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle\n[override]: https://docs.python.org/3/library/typing.html#typing.override",
|
||||||
"default": "error",
|
"default": "error",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue