Skip to content

Rules

invalid-function-defaults

What it does

Checks for invalid mutations of the __defaults__ attribute of a function.

Why is this bad?

Modifying the __defaults__ attribute with types different to the parameters can lead to runtime type errors.

Examples

Python
1
2
3
4
5
def foo(x: int = 1) -> int:
    return x

foo.__defaults__ = ("string",)
result = foo()  # Returns "string" but type checker thinks it's int

Default level: error.

Categories: runtime-modification.

See more

invalid-overload-implementation

What it does

Checks for invalid overload implementation.

Why is this bad?

Invalid overload implementation can lead to runtime errors.

Examples

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from typing import overload

@overload
def foo(x: int) -> str: ...
@overload
def foo(x: str) -> int: ...
def foo(x: int | str) -> int | str:
    return x

foo("1")

Default level: error.

Categories: type-checking-suppression.

See more

invalid-setattr

What it does

Checks for invalid setattr() usage.

Why is this bad?

setattr() bypasses type checking by allowing "dynamic" attribute assignment. You can assign any type to any attribute, which can lead to runtime type errors when the actual type doesn't match the declared type annotation.

Examples

Python
1
2
3
4
5
6
class Foo:
    def __init__(self) -> None:
        self.x: str = "hello"

foo = Foo()
setattr(foo, "x", 1)

Default level: error.

Categories: runtime-modification.

See more

mutable-generic-default

What it does

Checks for generic functions or methods that accept mutable objects as default parameter values.

Why is this bad?

When a generic function uses a mutable default value (like a list, dict, or set), that default is shared across all invocations of the function. This creates a scenario where the mutable object can accumulate values of different types from different calls.

Type checkers assume that list[T] only contains values of type T. However, when a mutable default is reused across calls with different type parameters, the list can contain values of multiple different types, leading to runtime type errors.

Examples

Python
1
2
3
4
5
6
7
8
9
def append_and_return[T](x: T, items: list[T] = []) -> list[T]:
    items.append(x)
    return items

int_list = append_and_return(42)
str_list = append_and_return("hello")

# This is a int at runtime but str at type check time.
value: str = str_list[0]

Default level: error.

Categories: runtime-modification.

See more

mutating-function-code-attribute

What it does

Checks for mutating the __code__ attribute of a function.

Why is this bad?

Modifying the __code__ attribute allows runtime modification of function internals, which can bypass type checking and lead to runtime type errors. Type checkers cannot analyze or verify operations performed through code objects.

Examples

Python
1
2
3
4
5
6
7
8
def foo(x: int) -> int:
    return 1

def bar(x: str) -> str:
    return "bar"

foo.__code__ = bar.__code__
# Now foo will return a string

Default level: error.

Categories: runtime-modification.

See more

mutating-globals-dict

What it does

Checks for mutations to the globals() dictionary.

Why is this bad?

Modifying the globals() dictionary allows runtime modification of global variables, which can bypass type checking and lead to runtime type errors. Type checkers cannot track or verify modifications made through the globals dictionary.

Examples

Python
1
2
3
4
5
6
x: int = 5

globals()['x'] = "hello"

# Type checker thinks `x` is an `int`, but it is now a string
result: int = x

Default level: error.

Categories: runtime-modification.

See more

callable-ellipsis-used

What it does

Checks for usage of ... (ellipsis) in the first argument of Callable type annotations.

Why is this bad?

Using Callable[..., ReturnType] indicates that the callable accepts any number of arguments of any type, which bypasses parameter type checking. This can lead to runtime type errors as the type checker cannot verify argument types or counts.

Examples

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from typing import Callable

def foo(callback: Callable[..., int]) -> int:
    return callback("wrong", "types")

def bar(x: int) -> int:
    return x

# This passes type checking but fails at runtime.
foo(bar)

Default level: warn.

Categories: type-checking-suppression.

See more

if-type-checking-used

What it does

Checks for usage of if TYPE_CHECKING: blocks from the typing module.

Why is this bad?

TYPE_CHECKING is False at runtime but True during static type checking. When used with an else clause where signatures don't match, the type checker validates against the if TYPE_CHECKING branch, but at runtime the else branch executes, causing runtime type errors that the type checker can't catch.

Examples

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    def get_value() -> int:
        ...
else:
    def get_value() -> str:
        return "hello"

result: int = get_value()  # Type checks, but returns str at runtime!

Default level: warn.

Categories: type-checking-suppression.

See more

mangled-dunder-instance-variable

What it does

Checks for explicit usage of mangled dunder instance variables in attribute access.

Why is this bad?

Python automatically mangles double-underscore (dunder) instance variables to _ClassName__variable to provide name privacy. When code explicitly uses the mangled form, it can bypass type checking by assigning different types to the mangled name than what the non-mangled variable expects.

Examples

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class HiddenDunderVariables:
    def __init__(self, x: int) -> None:
        self.__str_x = str(x)
        self._HiddenDunderVariables__str_x = x

    def get_str_x(self) -> str:
        return self.__str_x

# Here, x is a string at type check time, but an integer at runtime.
x = hidden_dunder_variables.get_str_x()

Default level: warn.

Categories: type-checking-suppression.

See more

type-checking-directive-used

What it does

Checks for usage of type checking directives in comments.

Why is this bad?

Type checking directives like # type: ignore suppress type checker warnings, which can hide potential type errors that may lead to runtime failures. These directives bypass the safety guarantees that type checking provides.

Examples

Python
1
2
3
# mypy / standard (PEP 484)
x = "string" + 123  # type: ignore
y = foo()  # type: ignore[attr-defined]

Default level: warn.

Categories: type-checking-suppression.

See more

typing-any-used

What it does

Checks for usage of typing.Any in type annotations.

Why is this bad?

Using typing.Any in type annotations can lead to runtime errors.

Examples

Python
1
2
3
4
5
6
from typing import Any

def foo(x: Any) -> Any:
    return x + 1

foo("1")

Default level: warn.

Categories: type-checking-suppression.

See more

typing-cast-used

What it does

Checks for usage of typing.cast() function calls.

Why is this bad?

typing.cast() tells the type checker to treat a value as a specific type without any runtime checks or validation. This can lead to runtime type errors if the cast is incorrect. Type checkers trust casts completely, so incorrect casts bypass all type safety guarantees.

Examples

Python
1
2
3
4
5
6
7
from typing import cast

def get_value() -> int | str:
    return "hello"

result = cast(int, get_value())
result + 1  # Type checks, but fails at runtime!

Default level: warn.

Categories: type-checking-suppression.

See more

typing-overload-used

What it does

Checks for usage of overloaded functions.

Why is this bad?

Using overloaded functions can lead to runtime errors. When users don't follow the correct overload implementation, it can lead to unexpected behavior.

Examples

Python
1
2
3
4
5
6
7
8
from typing import overload

@overload
def foo(x: int) -> str: ...
@overload
def foo(x: str) -> int: ...
def foo(x: int | str) -> int | str:
    return x

Default level: warn.

Categories: type-checking-suppression.

See more

typing-type-is-used

What it does

Checks for return types that use typing.TypeIs.

Why is this bad?

Using typing.TypeIs in return type annotations can lead to runtime type errors. Type checkers use TypeIs to narrow types, but incorrect implementation can bypass type safety guarantees.

Examples

Python
1
2
3
4
5
6
7
8
9
from typing import TypeIs

def is_int(x: object) -> TypeIs[int]:
    return True

value = "hello"

if is_int(value):
    result = value + 1  # Type checks but fails at runtime!

Default level: warn.

Categories: type-checking-suppression.

See more