Default level: error
detects byte strings in type annotation positions
Checks for byte-strings in type annotation positions.
Static analysis tools like ty can't analyse type annotations that use byte-string notation.
def test(): -> b"int":
...Use instead:
def test(): -> "int":
...Default level: error
detects calls to non-callable objects
Checks for calls to non-callable objects.
Calling a non-callable object will raise a TypeError at runtime.
4() # TypeError: 'int' object is not callableDefault level: error
detects when an argument is used as both a value and a type form in a call
Checks whether an argument is used as both a value and a type form in a call
Default level: error
detects conflicting declarations
Checks whether a variable has been declared as two conflicting types.
A variable with two conflicting declarations likely indicates a mistake. Moreover, it could lead to incorrect or ill-defined type inference for other code that relies on these variables.
Default level: error
Default level: error
detects cyclic class definitions
Checks for class definitions with a cyclic inheritance chain.
TODO #14889
Default level: error
detects division by zero
It detects division by zero.
Dividing by zero raises a ZeroDivisionError at runtime.
5 / 0Default level: error
detects class definitions with duplicate bases
Checks for class definitions with duplicate bases.
Class definitions with duplicate bases raise a TypeError at runtime.
Default level: error
detects forward type annotations with escape characters
TODO #14889
Default level: error
detects F-strings in type annotation positions
Checks for f-strings in type annotation positions.
Static analysis tools like ty can't analyse type annotations that use f-string notation.
def test(): -> f"int":
...Use instead:
def test(): -> "int":
...Default level: error
detects implicit concatenated strings in type annotations
Checks for implicit concatenated strings in type annotation positions.
Static analysis tools like ty can't analyse type annotations that use implicit concatenated strings.
def test(): -> "Literal[" "5" "]":
...Use instead:
def test(): -> "Literal[5]":
...Default level: error
detects class definitions whose MRO has conflicting __slots__
Checks for classes whose bases define incompatible __slots__.
Inheriting from bases with incompatible __slots__s
will lead to a TypeError at runtime.
Classes with no or empty __slots__ are always compatible:
class A: ...
class B:
__slots__ = ()
class C:
__slots__ = ("a", "b")
## fine
class D(A, B, C): ...Multiple inheritance from more than one different class
defining non-empty __slots__ is not allowed:
class A:
__slots__ = ("a", "b")
class B:
__slots__ = ("a", "b") # Even if the values are the same
## TypeError: multiple bases have instance lay-out conflict
class C(A, B): ...Dynamic (not tuple or string literal) __slots__ are not checked.
Additionally, classes inheriting from built-in classes with implicit layouts
like str or int are also not checked.
>>> hasattr(int, "__slots__")
False
>>> hasattr(str, "__slots__")
False
>>> class A(int, str): ...
Traceback (most recent call last):
File "<python-input-0>", line 1, in <module>
class A(int, str): ...
TypeError: multiple bases have instance lay-out conflictDefault level: error
detects class definitions with an inconsistent MRO
Checks for classes with an inconsistent method resolution order (MRO).
Classes with an inconsistent MRO will raise a TypeError at runtime.
Default level: error
detects index out of bounds errors
Checks for attempts to use an out of bounds index to get an item from a container.
Using an out of bounds index will raise an IndexError at runtime.
Default level: error
detects call arguments whose type is not assignable to the corresponding typed parameter
Detects call arguments whose type is not assignable to the corresponding typed parameter.
Passing an argument of a type the function (or callable object) does not accept violates the expectations of the function author and may cause unexpected runtime errors within the body of the function.
def func(x: int): ...
func("foo") # error: [invalid-argument-type]Default level: error
Default level: error
Invalid attribute access
Makes sure that instance attribute accesses are valid.
class C:
var: ClassVar[int] = 1
C.var = 3 # okay
C().var = 3 # error: Cannot assign to class variableDefault level: error
Default level: error
detects expressions used in with statements that don't implement the context manager protocol
TODO #14889
Default level: error
Default level: error
detects exception handlers that catch classes that do not inherit from BaseException
Checks for exception handlers that catch non-exception classes.
Catching classes that do not inherit from BaseException will raise a TypeError at runtime.
try:
1 / 0
except 1:
...Use instead:
try:
1 / 0
except ZeroDivisionError:
...This rule corresponds to Ruff's except-with-non-exception-classes (B030)
Default level: error
detects invalid generic classes
Checks for the creation of invalid generic classes
There are several requirements that you must follow when defining a generic class.
from typing import Generic, TypeVar
T = TypeVar("T") # okay
## error: class uses both PEP-695 syntax and legacy syntax
class C[U](Generic[T]): ...Default level: error
detects invalid legacy type variables
Checks for the creation of invalid legacy TypeVars
There are several requirements that you must follow when creating a legacy TypeVar.
from typing import TypeVar
T = TypeVar("T") # okay
Q = TypeVar("S") # error: TypeVar name must match the variable it's assigned to
T = TypeVar("T") # error: TypeVars should not be redefined
## error: TypeVar must be immediately assigned to a variable
def f(t: TypeVar("U")): ...Default level: error
detects invalid metaclass= arguments
Checks for arguments to metaclass= that are invalid.
Python allows arbitrary expressions to be used as the argument to metaclass=.
These expressions, however, need to be callable and accept the same arguments
as type.__new__.
def f(): ...
## TypeError: f() takes 0 positional arguments but 3 were given
class B(metaclass=f): ...Default level: error
detects invalid @overload usages
Checks for various invalid @overload usages.
The @overload decorator is used to define functions and methods that accepts different
combinations of arguments and return different types based on the arguments passed. This is
mainly beneficial for type checkers. But, if the @overload usage is invalid, the type
checker may not be able to provide correct type information.
Defining only one overload:
from typing import overload
@overload
def foo(x: int) -> int: ...
def foo(x: int | None) -> int | None:
return xOr, not providing an implementation for the overloaded definition:
from typing import overload
@overload
def foo() -> None: ...
@overload
def foo(x: int) -> int: ...Default level: error
detects default values that can't be assigned to the parameter's annotated type
Checks for default values that can't be assigned to the parameter's annotated type.
TODO #14889
Default level: error
detects invalid protocol class definitions
Checks for invalidly defined protocol classes.
An invalidly defined protocol class may lead to the type checker inferring
unexpected things. It may also lead to TypeErrors at runtime.
A Protocol class cannot inherit from a non-Protocol class;
this raises a TypeError at runtime:
>>> from typing import Protocol
>>> class Foo(int, Protocol): ...
...
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
class Foo(int, Protocol): ...
TypeError: Protocols can only inherit from other protocols, got <class 'int'>Default level: error
detects raise statements that raise invalid exceptions or use invalid causes
Checks for raise statements that raise non-exceptions or use invalid
causes for their raised exceptions.
Only subclasses or instances of BaseException can be raised.
For an exception's cause, the same rules apply, except that None is also
permitted. Violating these rules results in a TypeError at runtime.
def f():
try:
something()
except NameError:
raise "oops!" from f
def g():
raise NotImplemented from 42Use instead:
def f():
try:
something()
except NameError as e:
raise RuntimeError("oops!") from e
def g():
raise NotImplementedError from NoneDefault level: error
detects returned values that can't be assigned to the function's annotated return type
Detects returned values that can't be assigned to the function's annotated return type.
Returning an object of a type incompatible with the annotated return type may cause confusion to the user calling the function.
def func() -> int:
return "a" # error: [invalid-return-type]Default level: error
detects invalid arguments for super()
Detects super() calls where:
- the first argument is not a valid class literal, or
- the second argument is not an instance or subclass of the first argument.
super(type, obj) expects:
- the first argument to be a class,
- and the second argument to satisfy one of the following:
isinstance(obj, type)isTrueissubclass(obj, type)isTrue
Violating this relationship will raise a TypeError at runtime.
class A:
...
class B(A):
...
super(A, B()) # it's okay! `A` satisfies `isinstance(B(), A)`
super(A(), B()) # error: `A()` is not a class
super(B, A()) # error: `A()` does not satisfy `isinstance(A(), B)`
super(B, A) # error: `A` does not satisfy `issubclass(A, B)`Default level: error
Default level: error
detects invalid TYPE_CHECKING constant assignments
Checks for a value other than False assigned to the TYPE_CHECKING variable, or an
annotation not assignable from bool.
The name TYPE_CHECKING is reserved for a flag that can be used to provide conditional
code seen only by the type checker, and not at runtime. Normally this flag is imported from
typing or typing_extensions, but it can also be defined locally. If defined locally, it
must be assigned the value False at runtime; the type checker will consider its value to
be True. If annotated, it must be annotated as a type that can accept bool values.
Default level: error
detects invalid type forms
Checks for invalid type expressions.
TODO #14889
Default level: error
Default level: error
detects missing required arguments in a call
Checks for missing required arguments in a call.
Failing to provide a required argument will raise a TypeError at runtime.
def func(x: int): ...
func() # TypeError: func() missing 1 required positional argument: 'x'Default level: error
detects calls that do not match any overload
Checks for calls to an overloaded function that do not match any of the overloads.
Failing to provide the correct arguments to one of the overloads will raise a TypeError
at runtime.
@overload
def func(x: int): ...
@overload
def func(x: bool): ...
func("string") # error: [no-matching-overload]Default level: error
detects subscripting objects that do not support subscripting
Checks for subscripting objects that do not support subscripting.
Subscripting an object that does not support it will raise a TypeError at runtime.
4[1] # TypeError: 'int' object is not subscriptableDefault level: error
detects iteration over an object that is not iterable
Checks for objects that are not iterable but are used in a context that requires them to be.
Iterating over an object that is not iterable will raise a TypeError at runtime.
for i in 34: # TypeError: 'int' object is not iterable
passDefault level: error
detects multiple arguments for the same parameter
Checks for calls which provide more than one argument for a single parameter.
Providing multiple values for a single parameter will raise a TypeError at runtime.
def f(x: int) -> int: ...
f(1, x=2) # Error raised hereDefault level: error
detects raw strings in type annotation positions
Checks for raw-strings in type annotation positions.
Static analysis tools like ty can't analyse type annotations that use raw-string notation.
def test(): -> r"int":
...Use instead:
def test(): -> "int":
...Default level: error
Failed static assertion
Makes sure that the argument of static_assert is statically known to be true.
from ty_extensions import static_assert
static_assert(1 + 1 == 3) # error: evaluates to `False`
static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known truthinessDefault level: error
detects subclasses of final classes
Checks for classes that subclass final classes.
Decorating a class with @final declares to the type checker that it should not be subclassed.
from typing import final
@final
class A: ...
class B(A): ... # Error raised hereDefault level: error
detects calls passing too many positional arguments
Checks for calls that pass more positional arguments than the callable can accept.
Passing too many positional arguments will raise TypeError at runtime.
def f(): ...
f("foo") # Error raised hereDefault level: error
detects failed type assertions
Checks for assert_type() and assert_never() calls where the actual type
is not the same as the asserted type.
assert_type() allows confirming the inferred type of a certain value.
def _(x: int):
assert_type(x, int) # fine
assert_type(x, str) # error: Actual type does not match asserted typeDefault level: error
detects invalid super() calls where implicit arguments are unavailable.
Detects invalid super() calls where implicit arguments like the enclosing class or first method argument are unavailable.
When super() is used without arguments, Python tries to find two things:
the nearest enclosing class and the first argument of the immediately enclosing function (typically self or cls).
If either of these is missing, the call will fail at runtime with a RuntimeError.
super() # error: no enclosing class or function found
def func():
super() # error: no enclosing class or first argument exists
class A:
f = super() # error: no enclosing function to provide the first argument
def method(self):
def nested():
super() # error: first argument does not exist in this nested function
lambda: super() # error: first argument does not exist in this lambda
(super() for _ in range(10)) # error: argument is not available in generator expression
super() # okay! both enclosing class and first argument are availableDefault level: error
detects unknown keyword arguments in calls
Checks for keyword arguments in calls that don't match any parameter of the callable.
Providing an unknown keyword argument will raise TypeError at runtime.
def f(x: int) -> int: ...
f(x=1, y=2) # Error raised hereDefault level: error
detects references to unresolved attributes
Checks for unresolved attributes.
Accessing an unbound attribute will raise an AttributeError at runtime. An unresolved attribute is not guaranteed to exist from the type alone, so this could also indicate that the object is not of the type that the user expects.
Default level: error
detects unresolved imports
Checks for import statements for which the module cannot be resolved.
Importing a module that cannot be resolved will raise an ImportError
at runtime.
Default level: error
detects references to names that are not defined
Checks for references to names that are not defined.
Using an undefined variable will raise a NameError at runtime.
print(x) # NameError: name 'x' is not definedDefault level: error
detects boolean conversion where the object incorrectly implements __bool__
Checks for bool conversions where the object doesn't correctly implement __bool__.
If an exception is raised when you attempt to evaluate the truthiness of an object, using the object in a boolean context will fail at runtime.
class NotBoolable:
__bool__ = None
b1 = NotBoolable()
b2 = NotBoolable()
if b1: # exception raised here
pass
b1 and b2 # exception raised here
not b1 # exception raised here
b1 < b2 < b1 # exception raised hereDefault level: error
detects binary, unary, or comparison expressions where the operands don't support the operator
Checks for binary expressions, comparisons, and unary expressions where the operands don't support the operator.
Attempting to use an unsupported operator will raise a TypeError at
runtime.
Default level: error
detects a slice step size of zero
Checks for step size 0 in slices.
A slice with a step size of zero will raise a ValueError at runtime.
l = list(range(10))
l[1:10:0] # ValueError: slice step cannot be zeroDefault level: warn
detects calls to possibly unbound methods
Checks for calls to possibly unbound methods.
Calling an unbound method will raise an AttributeError at runtime.
Default level: warn
detects ignore comments that use invalid syntax
Checks for type: ignore and ty: ignore comments that are syntactically incorrect.
A syntactically incorrect ignore comment is probably a mistake and is useless.
a = 20 / 0 # type: ignoreeUse instead:
a = 20 / 0 # type: ignoreDefault level: warn
detects references to possibly unbound attributes
Checks for possibly unbound attributes.
Attempting to access an unbound attribute will raise an AttributeError at runtime.
Default level: warn
detects possibly unbound imports
Checks for imports of symbols that may be unbound.
Importing an unbound module or name will raise a ModuleNotFoundError
or ImportError at runtime.
Default level: warn
detects redundant cast calls
Detects redundant cast calls where the value already has the target type.
These casts have no effect and can be removed.
def f() -> int:
return 10
cast(int, f()) # RedundantDefault level: warn
detects usages of reveal_type without importing it
Checks for calls to reveal_type without importing it.
Using reveal_type without importing it will raise a NameError at runtime.
TODO #14889
Default level: warn
detects ty: ignore comments that reference unknown rules
Checks for ty: ignore[code] where code isn't a known lint rule.
A ty: ignore[code] directive with a code that doesn't match
any known rule will not suppress any type errors, and is probably a mistake.
a = 20 / 0 # ty: ignore[division-by-zer]Use instead:
a = 20 / 0 # ty: ignore[division-by-zero]Default level: ignore
detects references to possibly undefined names
Checks for references to names that are possibly not defined.
Using an undefined variable will raise a NameError at runtime.
for i in range(0):
x = i
print(x) # NameError: name 'x' is not definedDefault level: ignore
detects unused type: ignore comments
Checks for type: ignore or ty: ignore directives that are no longer applicable.
A type: ignore directive that no longer matches any diagnostic violations is likely
included by mistake, and should be removed to avoid confusion.
a = 20 / 2 # ty: ignore[division-by-zero]Use instead:
a = 20 / 2