| --- |
| title: Pyrefly Error Kinds |
| |
| description: Pyrefly error categories and suppression codes |
| --- |
| |
| {/* |
| * Copyright (c) Meta Platforms, Inc. and affiliates. |
| * |
| * This source code is licensed under the MIT license found in the |
| * LICENSE file in the root directory of this source tree. |
| */} |
| |
| # Pyrefly Error Kinds |
| |
| An _error kind_ categorizes an error by the part of the typing specification |
| that an error is related to. Every error has exactly one kind. |
| |
| The main use of error kinds is as short names ("slugs") that can be used in |
| error suppression comments. |
| |
| Coming from another type checker? See the |
| [mypy error-code mapping](./migrate/mypy/error-codes.mdx) and the |
| [Pyright diagnostic mapping](./migrate/pyright/diagnostics-reference.mdx) for how those |
| diagnostics correspond to the kinds below. |
| |
| Diagnostics have several possible severity levels, which can be [configured](../configuration#errors) in `pyrefly.toml` or via CLI: |
| - **ignore**: the diagnostic is not emitted |
| - **info**: the diagnostic shows up blue in the IDE |
| - **warn**: the diagnostic shows up yellow in the IDE |
| - **error**: the diagnostic shows up red in the IDE |
| |
| The default severity for diagnostics is `error` unless otherwise noted. Diagnostics with default severity `ignore` must be explicitly enabled to be emitted. |
| |
| By default, `pyrefly check` only displays diagnostics with severity `error` or higher. |
| Use the [`min-severity`](../configuration#min-severity) config option or `--min-severity` CLI flag to also show lower-severity diagnostics (e.g. `--min-severity warn`). |
| |
| By default, only `error`-level diagnostics cause a nonzero exit code in the CLI. When [`min-severity`](../configuration#min-severity) is set, any diagnostic at or above that threshold causes a nonzero exit. |
| |
| In the IDE, diagnostics below `error` severity are only shown for files that are currently open in the editor. |
| |
| ## abstract-method-call |
| |
| This error is raised when code attempts to invoke a method decorated with |
| `@abstractmethod`. Abstract methods have no implementation, so calling them is |
| always invalid, even if the signature would otherwise match. |
| |
| ```python |
| from abc import ABC, abstractmethod |
| |
| class Base(ABC): |
| @classmethod |
| @abstractmethod |
| def build(cls) -> "Base": ... |
| |
| Base.build() |
| # Cannot call abstract method `Base.build` [abstract-method-call] |
| ``` |
| |
| ## assert-type |
| |
| An `assert-type` error is raised when a `typing.assert_type()` call fails. |
| |
| ## bad-argument-count |
| |
| This error arises when a function is called with the wrong number of arguments. |
| |
| ```python |
| def takes_three(one: int, two: int, three: int) -> complex: |
| ... |
| |
| takes_three(1, 2, 3, 4) # Expected 3 positional arguments, got 4 [bad-argument-count] |
| ``` |
| |
| Note that `missing-argument` will be raised if pyrefly can identify that |
| specific arguments are missing. As such, this error is more likely to appear |
| when too many args are supplied, rather than too few. |
| |
| This example shows both kinds of errors: |
| |
| ```python |
| from typing import Callable |
| def apply(f: Callable[[int, int], int]) -> int: |
| return f(1) # Expected 1 more positional argument [bad-argument-count] |
| apply() # Missing argument `f` in function `apply` [missing-argument] |
| ``` |
| |
| ## bad-argument-type |
| |
| This error indicates that the function was called with an argument of the wrong |
| type. |
| |
| ```python |
| def example(x: int) -> None: |
| ... |
| example("one") # Argument `Literal['two']` is not assignable to parameter `x` with type `int` in function `example` [bad-argument-type] |
| ``` |
| |
| This can also happen with `*args` and `**kwargs`: |
| |
| ```python |
| def bad_args(*args: int) -> None: |
| ... |
| |
| bad_args(1, "two") # Argument `Literal['two']` is not assignable to parameter with type `int` in function `bad_args` [bad-argument-type] |
| ``` |
| |
| ```python |
| def bad_kwargs(**kwargs: int) -> None: |
| ... |
| |
| bad_args(x=1, y="two") # Keyword argument `y` with type `Literal['two']` is not assignable to kwargs type `int` in function `bad_kwargs` [bad-argument-type] |
| ``` |
| |
| ## bad-assignment |
| |
| The most common cause of this error is attempting to assign a value that conflicts with the variable's type annotation. |
| |
| ```python |
| x: str = 1 # `Literal[1]` is not assignable to `str` [bad-assignment] |
| ``` |
| |
| However, it can occur in several other situations. |
| |
| Here, `x` is marked as `Final`, so assigning a new value to it is an error. |
| |
| ```python |
| from typing import Final |
| x: Final = 1 |
| x = 2 # `x` is marked final [bad-assignment] |
| ``` |
| |
| In another case, attempting to annotate an assignment to an instance attribute raises this error. |
| |
| ```python |
| class A: |
| x: int |
| a = A() |
| a.x: int = 2 # Type cannot be declared in assignment to non-self attribute `a.x` [bad-assignment] |
| ``` |
| |
| ## bad-class-definition |
| |
| This error indicates that there is something wrong with the class definition. |
| It tends to be a bit rarer, since most issues would be tagged with other error kinds, such as |
| `annotation-mismatch` or one of the function errors. |
| Inheritance has its own complexities, so it has its own error kind called `invalid-inheritance`. |
| |
| One place you may see it is dynamic class generation: |
| |
| ```python |
| from enum import Enum |
| Ex = Enum("Ex", [("Red", 1), ("Blue", 2), ("Red", 3)]) # Duplicate field `Red` [bad-class-definition] |
| ``` |
| |
| However, it is best practice to use the class syntax if possible, which doesn't treat duplicate names as an error. |
| |
| It also covers decorators applied to a class kind they cannot be applied to, |
| such as `@dataclass` on a `Protocol`, `@disjoint_base` on a `TypedDict` or |
| `Protocol`, or `@runtime_checkable` on a non-`Protocol` class. |
| |
| ## bad-context-manager |
| |
| This error occurs when a type that cannot be used as a context manager appears in a `with` statement. |
| |
| ```python |
| class A: |
| def __enter__(self): ... |
| with A(): ... # `A` is missing an `__exit__` method! |
| ``` |
| |
| ## bad-dataclass-descriptor |
| |
| A dataclass field is typed as a descriptor whose read-back type does not match what the |
| synthesized `__init__` writes. |
| |
| A **data descriptor** defines `__set__` and/or `__delete__`. It always takes priority over the |
| instance dictionary, so when one is used as a dataclass field the class-level descriptor acts as |
| the field's default. If it also defines `__get__`, reading the field yields the `__get__` return |
| type while writing it goes through `__set__`, so the two must agree: |
| |
| ```python |
| from dataclasses import dataclass |
| |
| class Desc: |
| def __get__(self, obj, cls) -> int: ... |
| def __set__(self, obj, value: str) -> None: ... |
| |
| @dataclass |
| class C: |
| x: Desc = Desc() # `__get__` returns `int`, but `__set__` accepts `str` [bad-dataclass-descriptor] |
| ``` |
| |
| A **non-data descriptor** defines `__get__` but neither `__set__` nor `__delete__`. It does not |
| take priority over the instance dictionary, so the synthesized `__init__` writes straight into |
| that dictionary and shadows the class-level descriptor. Every later read returns the raw value |
| rather than calling `__get__`: |
| |
| ```python |
| from dataclasses import dataclass |
| |
| class Desc: |
| def __get__(self, obj, cls) -> int: ... |
| |
| @dataclass |
| class C: |
| x: Desc = Desc() # instance attribute shadows the descriptor [bad-dataclass-descriptor] |
| ``` |
| |
| A non-data descriptor whose `__get__` returns `Self` or its own class is exempt, since the |
| shadowing value then has the same type as the read-back value. |
| |
| ## bad-dunder-all |
| |
| This error occurs when `__all__` is explicitly defined for a module but contains an entry that cannot be found in the module's definitions, wildcard imports, or submodules (for `__init__.py` files). |
| |
| ```python |
| __all__ = ["x", "y"] # Name `y` is listed in `__all__` but is not defined in the module |
| x = 5 |
| ``` |
| |
| To fix this error, either define the missing name or remove it from `__all__`: |
| |
| ```python |
| __all__ = ["x", "y"] |
| x = 5 |
| y = 10 # Now `y` is defined |
| ``` |
| |
| ## bad-function-definition |
| |
| Like `bad-class-definition`, this error kind is uncommon because other error kinds are used for more specific issues. |
| For example, argument order is enforced by the parser, so `def f(x: int = 1, y: str)` is a `parse-error`. |
| |
| It also covers decorators applied to a function when the typing spec only |
| allows them on classes, such as `@disjoint_base` on a function. |
| |
| ## bad-index |
| |
| Attempting to access a container with an incorrect index. |
| This only occurs when Pyrefly can statically verify that the index is incorrect, such as with a fixed-length tuple. |
| |
| ```python |
| def add_three(x: tuple[int, int]) -> int: |
| return x[0] + x[1] + x[2] # Error: index 2 is out of range. |
| ``` |
| |
| Pyrefly also knows the keys of `TypedDict`s, but those have their own error kind. |
| |
| ## bad-instantiation |
| |
| This error occurs when attempting to instantiate a class that cannot be instantiated, such as a protocol: |
| |
| ```python |
| from typing import Protocol |
| class C(Protocol): ... |
| C() # bad-instantiation |
| ``` |
| |
| ## bad-keyword-argument |
| |
| bad-keyword-argument pops up when a keyword argument is given multiple values: |
| |
| ```python |
| def f(x: int) -> None: |
| pass |
| f(x=1, x=2) |
| ``` |
| |
| However, this is often accompanied by a `parse-error` for the same issue. |
| |
| ## bad-match |
| |
| This error is used in two cases. |
| |
| The first is when there is an issue with a `match` statement. For example, `Ex` only has 2 fields but the `case` lists 3: |
| |
| ```python |
| class Ex: |
| __match_args__ = ('a', 'b') |
| def __init__(self, a: int, b: str) -> None: |
| self.a = a |
| self.b = b |
| |
| def do(x: Ex) -> None: |
| match x: |
| case Ex(a, b, c): |
| print("This is an error") |
| ``` |
| |
| It is also used when `__match_args__` is defined incorrectly. It must be a tuple of the names of the class's attributes as literal strings. |
| For class `Ex` in the previous example, `__match_args__ = ('a', 'c')` would be an error because `Ex.c` does not exist. |
| |
| ## bad-override |
| |
| When a subclass overrides a field or method of its base class, care must be taken that the override won't cause problems. |
| |
| Some of these are obvious: |
| |
| ```python |
| class Base: |
| def f(self, a: int) -> None: |
| pass |
| |
| class NoArg(Base): |
| def f(self) -> None: |
| pass |
| |
| class WrongType(Base): |
| def f(self, a: str) -> None: |
| pass |
| |
| def uses_f(b: Base) -> None: |
| b.f(1) |
| ``` |
| |
| These errors are rather obvious: `uses_f` will fail if given a `NoArg` or `WrongType` instance, because those methods don't expect an `int` argument! |
| |
| The guiding idea here is the [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle), the idea that a subclass can stand in for a base class at any point without breaking the program. |
| |
| This can be a little subtle at first blush. Consider: |
| |
| ```python |
| class Base: |
| def f(self, a: int) -> None: |
| pass |
| |
| class Sub(Base): |
| def f(self, a: float) -> None: |
| pass |
| ``` |
| |
| Is this OK? Yes! `int` is treated as a subclass of `float`, or to put it another way, a function that accepts `float` can accept every `int`. |
| That means everywhere that we call `Base.f` can safely call `Sub.f`. |
| |
| The opposite case, where `Base.f` takes `float` and `Sub.f` takes `int`, is an error because `Sub.f` cannot accept every `float` value. |
| |
| Note that bad overrides caused by inconsistent parameter names are separately reported as [bad-override-param-name](#bad-override-param-name), and bad overrides caused by mutable attribute type changes are separately reported as [bad-override-mutable-attribute](#bad-override-mutable-attribute). |
| |
| ## bad-override-mutable-attribute |
| |
| Arises when a subclass overrides a mutable (read-write) attribute of a parent class with an incompatible type. |
| Mutable attributes require invariant types — the child's type must be exactly compatible with the parent's — because the attribute can be both read and written through a reference to the parent class. |
| |
| ```python |
| class Base: |
| x: int | str |
| |
| class Sub(Base): |
| x: int # Error: narrows the type |
| ``` |
| |
| This is unsafe because code that holds a `Base` reference can write any `int | str` value to `x`, but `Sub` only expects `int`: |
| |
| ```python |
| def f(b: Base) -> None: |
| b.x = "hello" # valid for Base, but breaks Sub's invariant |
| |
| f(Sub()) |
| ``` |
| |
| This is a sub-kind of [bad-override](#bad-override): suppressing `bad-override` also suppresses this error. |
| Other type checkers like mypy do not enforce this check by default, so this error kind can be selectively disabled if desired. |
| |
| ## bad-override-param-name |
| |
| Arises when a subclass overrides a method of its base class while changing the name of a positional parameter. |
| This is a sub-kind of [bad-override](#bad-override): suppressing `bad-override` also suppresses this error. |
| Changing the name of a parameter breaks callers that pass in an argument by name: |
| |
| ```python |
| class Base: |
| def f(self, a: int) -> None: |
| pass |
| |
| class Sub(Base): |
| def f(self, b: int) -> None: |
| pass |
| |
| def f(base: Base): |
| base.f(a=0) |
| |
| f(Sub()) # oops! |
| ``` |
| |
| ## bad-param-name-override |
| |
| Deprecated: this error code has been renamed to [bad-override-param-name](#bad-override-param-name). |
| The old name is still accepted in suppression comments and configuration for backwards compatibility. |
| |
| ## bad-raise |
| |
| In a `raise` statement of the form `raise x from y`, `x` must be an exception, and `y` must be an exception or `None`. |
| |
| ```python |
| def bad_raise() -> None: |
| raise Exception() # ok |
| raise 1 # error |
| raise Exception() from None # ok |
| raise Exception() from Exception() # ok |
| raise Exception() from 1 # error |
| ``` |
| |
| ## bad-return |
| |
| Arises when a function does not return a value that is compatible with the function's return type annotation. |
| |
| ```python |
| def bad_return() -> None: |
| return 1 |
| ``` |
| |
| Real-world examples are often less obvious, of course, due to complex control flow and type relationships. |
| |
| This error is also raised for generator functions: |
| |
| ```python |
| from typing import Generator |
| # Generator has 3 types: the yield, send, and return types. |
| def bad_gen() -> Generator[int, None, str]: |
| yield 1 |
| return 2 # should be a str! |
| ``` |
| |
| ## bad-singledispatch-register |
| |
| A `functools.singledispatch` implementation is registered with a dispatch type that is not a subtype of the fallback function's first parameter. Since that parameter defines the function's declared domain, the registration is outside it and can never be dispatched to through type-correct calls (often meaning the fallback was annotated too narrowly). |
| |
| ```python |
| from functools import singledispatch |
| |
| @singledispatch |
| def f(arg: int) -> None: ... |
| |
| @f.register |
| def _(arg: str) -> None: ... # bad-singledispatch-register: `str` is not a subtype of `int` |
| ``` |
| |
| ## bad-specialization |
| |
| "Specialization" refers to instantiating a generic type with a concrete type. |
| For example, `list` is a generic type, and `list[int]` is that type specialized with `int`. |
| |
| Each generic type has an expected number of type vars, and each type var can be bound or constrained. |
| Attempting to use specialize a generic type in a way that violates these specifications will result in a `bad-specialization` error: |
| |
| ```python |
| x: list[int, str] # Error: expected 1 type argument, got 2. |
| |
| class A[T: str]: ... |
| y: A[int] # Error: `int` is not assignable to `str`. |
| ``` |
| |
| ## bad-typed-dict |
| |
| This error is reported when a `TypedDict` definition includes an unsupported keyword argument. |
| |
| ```python |
| from typing import TypedDict |
| |
| # This is an error because `foo` is not a valid keyword. |
| class InvalidTD(TypedDict, foo=1): |
| x: int |
| |
| # This is valid. |
| class ValidTD(TypedDict, total=False): |
| x: int |
| ``` |
| |
| ## bad-typed-dict-key |
| |
| This error arises when `TypedDict`s are used with incorrect keys, such as a key that does not exist in the `TypedDict`. |
| |
| ```python |
| from typing import TypedDict |
| |
| class Ex(TypedDict): |
| a: int |
| b: str |
| |
| def test(x: Ex) -> None: |
| # These two keys don't exist |
| x.nope |
| x["wrong"] |
| # TypedDict keys must be strings! |
| x[1] |
| ``` |
| |
| ## bad-unpacking |
| |
| An error caused by unpacking, such as attempting to unpack a list, tuple, or iterable into the wrong number of variables. |
| |
| ```python |
| def two_elems() -> tuple[int, str]: |
| return (1, "two") |
| |
| a, b, c = two_elems() |
| ``` |
| |
| Note that pyrefly can only report this error if it knows how many elements the thing being unpacked has. |
| |
| ```python |
| # A bare `tuple` could have any number of elements |
| def two_elems() -> tuple: |
| return (1, "two") |
| |
| a, b, c = two_elems() |
| ``` |
| |
| ## column-schema-mismatch |
| |
| A Polars DataFrame's data columns do not name exactly the columns declared by its `schema=`. A declared `schema` is the authoritative column set, so a data column that is missing from it or absent from the data raises `ValueError` or `KeyError` at runtime. |
| |
| ```python |
| import polars as pl |
| |
| pl.DataFrame({"x": [1]}, schema={"a": pl.Int64}) # data column `x` is not in the schema, and `a` has no data |
| ``` |
| |
| ## column-type-mismatch |
| |
| A Polars DataFrame column literal has an element whose type does not fit the column's dtype. Polars infers the dtype from the first element and requires every later element to fit it, so a mismatch raises `TypeError` at runtime. |
| |
| ```python |
| import polars as pl |
| |
| pl.DataFrame({"a": [1, 2.0]}) # column `a` is `int`, so the `float` does not fit |
| ``` |
| |
| ## deprecated |
| |
| Default severity: `warn` |
| |
| This warning occurs on usage of a deprecated class or function. |
| |
| ```python |
| from warnings import deprecated |
| @deprecated("deprecated") |
| def f(): ... |
| f() # deprecated! |
| ``` |
| |
| ## direct-abstract-base-instantiation |
| |
| Default severity: `warn` |
| |
| This diagnostic is raised when code instantiates a class that directly extends |
| `abc.ABC` or directly uses `abc.ABCMeta`, even if the class has no abstract |
| methods. Python permits such classes to be instantiated, and some libraries use |
| `ABC` without prohibiting construction. |
| |
| ## division-by-zero |
| |
| Default severity: `warn` |
| |
| Division, floor division, or modulo by a literal zero value. This catches cases where the divisor is the literal `0` or a variable with type `Literal[0]`, which would always raise `ZeroDivisionError` at runtime. |
| |
| ```python |
| x = 10 / 0 # error: division by zero |
| y = 10 // 0 # error: division by zero |
| z = 10 % 0 # error: division by zero |
| ``` |
| |
| ## duplicate-column |
| |
| A Polars projection produces more than one column with the same name. Polars rejects duplicate output names when an eager projection runs or a lazy projection is collected. |
| |
| ```python |
| import polars as pl |
| |
| df = pl.DataFrame({"a": [1]}) |
| df.select("a", "a") |
| ``` |
| |
| ## empty-body |
| |
| Default severity: `ignore` |
| |
| A function body consists only of `...` even though the function declares a |
| return type that `None` is not assignable to. |
| |
| Empty ellipsis bodies are allowed in stub files, protocol methods, abstract |
| methods, overload declarations, and `if TYPE_CHECKING` blocks. |
| |
| ```python |
| def f() -> int: ... # error: empty body |
| ``` |
| |
| ## explicit-any |
| |
| Default severity: `ignore` |
| |
| `typing.Any` was written explicitly in an annotation. This diagnostic is useful for codebases that want to prevent `Any` from silently allowing arbitrary operations. |
| |
| ```python |
| from typing import Any |
| |
| def f(x: Any) -> Any: # explicit-any on both annotations |
| return x |
| |
| # Fix: |
| def f(x: object) -> object: |
| return x |
| ``` |
| |
| ## implicit-abstract-class |
| |
| Default severity: `ignore` |
| |
| Pyrefly emits this error when a class that inherits from an abstract class but is not itself explicitly abstract (for example, it does not directly inherit from `abc.ABC` or use `abc.ABCMeta`) has unimplemented abstract members. Such classes cannot be instantiated at runtime. To resolve the issue, explicitly declare the class as abstract or provide concrete implementations. |
| |
| ```python |
| from abc import ABC, abstractmethod |
| class A(ABC): |
| @abstractmethod |
| def f(self) -> int: ... |
| class B(A): ... # Error: `B` cannot be instantiated due to unimplemented abstract method `f`. |
| ``` |
| |
| Two possible fixes: |
| |
| ```python |
| # 1. If `B` is not meant to be instantiable, explicitly declare it as abstract. |
| class B(A, ABC): ... |
| ``` |
| |
| ```python |
| # 2. Or, implement `f` to make `B` instantiable. |
| class B(A): |
| def f(self) -> int: |
| return 0 |
| ``` |
| |
| ## implicit-any |
| |
| Default severity: `ignore` |
| |
| Umbrella error code for cases where Pyrefly infers an implicit `Any`. Suppressing or enabling `implicit-any` cascades to every sub-kind below — useful when you want to surface (or silence) all implicit-`Any` cases without listing them individually. |
| |
| Most concrete diagnostics are emitted under one of the more specific sub-kinds: |
| - [`implicit-any-attribute`](#implicit-any-attribute) |
| - [`implicit-any-empty-container`](#implicit-any-empty-container) |
| - [`implicit-any-lambda`](#implicit-any-lambda) |
| - [`implicit-any-parameter`](#implicit-any-parameter) |
| - [`implicit-any-type-argument`](#implicit-any-type-argument) |
| |
| ## implicit-any-attribute |
| |
| Default severity: `ignore` |
| |
| An attribute is implicitly inferred to be `Any | None` (or `tuple[Any, ...]`) because it was assigned `None` or `()` in a method without an explicit type annotation. Add an annotation so the attribute's type is determined at the assignment site rather than leaking `Any` into downstream uses. |
| |
| This is a sub-kind of [implicit-any](#implicit-any): suppressing `implicit-any` also suppresses this error. |
| |
| ```python |
| class C: |
| def __init__(self): |
| self.a = None # implicit-any-attribute (type: None | Any) |
| self.b = () # implicit-any-attribute (type: tuple[Any, ...]) |
| |
| # Fix: |
| class C: |
| a: int | None |
| b: tuple[int, ...] |
| def __init__(self): |
| self.a = None |
| self.b = () |
| ``` |
| |
| ## implicit-any-empty-container |
| |
| Default severity: `ignore` |
| |
| An empty container literal (`[]`, `{}`) couldn't be inferred from context and was pinned to a container of `Any`. Provide a type annotation, initialize with a non-empty value, or use the value in a way that lets Pyrefly infer its element type. |
| |
| This is a sub-kind of [implicit-any](#implicit-any): suppressing `implicit-any` also suppresses this error. |
| |
| ```python |
| x = [] # implicit-any-empty-container — type: list[Any] |
| |
| # Fix: |
| x: list[int] = [] |
| # or |
| x = [1, 2, 3] |
| ``` |
| |
| ## implicit-any-lambda |
| |
| Default severity: `ignore` |
| |
| A lambda parameter whose type cannot be inferred is treated as an implicit `Any`, hiding type errors in the lambda body and at its call sites. |
| |
| When the lambda is used in a typed context (e.g. assigned to a `Callable` or passed to a function that expects one), the parameters are inferred from that context and no error is reported. |
| |
| ```python |
| f = lambda x: x # error: implicit-any-lambda |
| |
| from typing import Callable |
| g: Callable[[int], int] = lambda x: x # OK: `x` is inferred as `int` from context |
| ``` |
| |
| ## implicit-any-parameter |
| |
| Default severity: `ignore` |
| |
| A function parameter has no type annotation, so Pyrefly treats it as `Any`. Add an annotation. The `self` and `cls` parameters of methods are excluded from this check. |
| |
| ```python |
| def f(x): |
| return x + 1 |
| |
| # Fix: |
| def f(x: int) -> int: |
| return x + 1 |
| ``` |
| |
| ## implicit-any-type-argument |
| |
| Default severity: `ignore` |
| |
| A generic class, type alias, or special form (`tuple`, `Callable`, `type`) was used without explicit type arguments, so Pyrefly defaulted the missing type parameters to `Any`. Provide explicit arguments, or declare a default for the relevant type variable. |
| |
| This is a sub-kind of [implicit-any](#implicit-any): suppressing `implicit-any` also suppresses this error. |
| |
| ```python |
| def f(xs: list) -> tuple: # implicit-any-type-argument on both `list` and `tuple` |
| return tuple(xs) |
| |
| # Fix: |
| def f(xs: list[int]) -> tuple[int, ...]: |
| return tuple(xs) |
| ``` |
| |
| ## implicit-bool |
| |
| Default severity: `ignore` |
| |
| This error is emitted when a non-`bool` value is used in a boolean context. Enable it to require |
| explicit checks for values whose truthiness could conflate distinct cases, such as `None` and zero |
| or `None` and an empty container. An explicit `bool(...)` conversion is allowed. |
| |
| ```python |
| def get_temperature() -> int | None: |
| return 0 |
| |
| temperature = get_temperature() |
| if not temperature: # implicit-bool |
| raise RuntimeError("Temperature sensor unavailable") |
| |
| # Fix: |
| if temperature is None: |
| raise RuntimeError("Temperature sensor unavailable") |
| ``` |
| |
| ## implicit-import |
| |
| Default severity: `warn` |
| |
| This error is emitted when a submodule is accessed through a parent package without being explicitly imported in the current file. |
| |
| While Python’s global module cache (`sys.modules`) might allow this to work if another part of your program performed the import earlier, relying on this side effect is fragile. If that external code is refactored or removed, your code will crash at runtime with an `AttributeError`. |
| |
| To fix this, always use an explicit import for the submodule you want to access. |
| |
| ```python |
| import urllib |
| # error: 'urllib' has no attribute 'request' (unless imported elsewhere) |
| urllib.request.urlopen(...) |
| |
| # Fix: |
| import urllib.request |
| urllib.request.urlopen(...) |
| |
| ``` |
| |
| ## implicit-reexport |
| |
| Default severity: `ignore` |
| |
| This error is emitted when you import a name from a module that only made it |
| available through a plain `import` or `from ... import ...` statement. Per the |
| [typing spec](https://typing.python.org/en/latest/spec/distributing.html#import-conventions), |
| such a name is not part of the module's public interface, so relying on it is |
| fragile: the intermediate module may stop importing it at any time. |
| |
| A module explicitly re-exports a name when it is redundantly aliased |
| (`from x import y as y` or `import x as x`), listed in `__all__`, or brought in |
| via a wildcard (`from x import *`). |
| |
| ```python |
| # foo.py |
| a = 1 |
| |
| # bar.py |
| from foo import a # `a` is not re-exported from `bar` |
| |
| # baz.py |
| from bar import a # error: `a` is not exported from module `bar` |
| |
| # Fix in bar.py: |
| from foo import a as a # or add `a` to `__all__` |
| ``` |
| |
| ## implicitly-defined-attribute |
| |
| Default severity: `ignore` |
| |
| An attribute was implicitly defined by assignment to `self` in a method that we |
| do not recognize as always executing. We recognize constructors and some test |
| setup methods; we will emit an error for any attributes defined by assignment |
| in other methods. |
| |
| ```python |
| class C: |
| def __init__(self): |
| self.x = 0 # no error, `__init__` always executes |
| def f(self): |
| self.y = 0 # error, `y` may be undefined if `f` does not execute |
| ``` |
| |
| ## incompatible-comparison |
| |
| Default severity: `ignore` |
| |
| This error is raised when Pyrefly can prove that an equality (`==`) or inequality |
| (`!=`) comparison is made between incompatible built-in types (for example, `int` |
| versus `str`). This check is currently limited to a small allowlist: numeric |
| types (including `decimal.Decimal`), bytes-like types, set-like types, and `str`. |
| It does not analyze unions, `None`, or user-defined classes. |
| |
| ```python |
| x: int = 1 |
| y: str = "" |
| if x == y: ... # Comparison `==` between incompatible types `int` and `str` [incompatible-comparison] |
| ``` |
| |
| ## incompatible-overload-residual |
| |
| This error is raised when we match an overloaded function against a type containing a type variable and cannot find a consistent type for that variable. |
| |
| This usually indicates a higher-order call where each overload branch implies incompatible constraints for the same type variable. |
| |
| ```python |
| from typing import Callable, overload |
| |
| @overload |
| def f(x: int) -> float: ... |
| @overload |
| def f(x: str) -> str: ... |
| def f(x): ... |
| |
| def project[S](func: Callable[[S], S], y: S) -> Callable[[], S]: |
| return lambda: y |
| |
| project(f, 1) # Overload type was not compatible with solved type variables: S = int |
| ``` |
| |
| ## inconsistent-inheritance |
| |
| When a class inherits from multiple base classes, the inherited fields must be consistent. |
| |
| Example: |
| ```python |
| class A: |
| f: str |
| class B: |
| f: int |
| class C(A, B): ... # error, the field `f` is inconsistent |
| ``` |
| |
| ## inconsistent-overload |
| |
| The signature of a function overload is inconsistent with the implementation. |
| See the [typing specification](https://typing.python.org/en/latest/spec/overload.html#implementation-consistency) |
| for details on the consistency checks Pyrefly performs. |
| |
| Example: |
| ```python |
| from typing import overload |
| @overload |
| def f(x: int) -> int: ... |
| @overload |
| def f(x: str) -> str: ... # error, overload accepts `str` but implementation only accepts `int` |
| def f(x: int) -> int | str: |
| return x |
| ``` |
| |
| ## inconsistent-overload-default |
| |
| In an overloaded function, the type of a parameter in an overload signature is inconsistent with |
| its default value in the implementation. |
| |
| Example: |
| ```python |
| from typing import Literal, overload |
| @overload |
| def f(x: Literal[True] = ...) -> None: ... # error, `x` has default `False`, which is inconsistent with type `Literal[True]` |
| @overload |
| def f(x: Literal[False]) -> int: ... |
| def f(x: bool = False) -> int | None: |
| return 0 if x else None |
| ``` |
| |
| ## internal-error |
| |
| Ideally you'll never see this one. If you do, please consider [filing a bug](https://github.com/facebook/pyrefly/issues). |
| |
| ## invalid-abstract-method |
| |
| Default severity: `ignore` |
| |
| Pyrefly emits this error when a class that is not abstract (does not inherit from `abc.ABC`, use `abc.ABCMeta`, have any transitive abstract base, or define a `Protocol` or `NewType`) defines a method decorated with `@abstractmethod`. Such a class is directly instantiable at runtime, yet contains an unimplemented method — a likely programming mistake. |
| |
| ```python |
| from abc import abstractmethod |
| |
| class Foo: |
| @abstractmethod # Error: `Foo.fn` is decorated with `@abstractmethod` but `Foo` is not an abstract class |
| def fn(self) -> int: ... |
| ``` |
| |
| To fix the issue, either inherit from `abc.ABC` to make the class abstract, define a `Protocol` if the class is an interface, or remove the `@abstractmethod` decorator and provide a concrete implementation. |
| |
| ## invalid-annotation |
| |
| There are several reasons why an annotation may be invalid. The most common case is misusing a typing special form, such as `typing.Final`, `typing.ClassVar`, `typing.ParamSpec`, and so on. |
| |
| Even when no configuration file is present, this diagnostic is shown as a warning in the IDE. |
| |
| ```python |
| from typing import * |
| |
| # Final must have a value |
| a: Final |
| # ClassVar can only be used in a class body |
| b: ClassVar[int] = 1 |
| ``` |
| |
| The error messages will explain how the special form is being misused. Consult the [typing docs](https://docs.python.org/3/library/typing.html) and [typing spec](https://typing.python.org/en/latest/spec/) for more information. |
| |
| ## invalid-argument |
| |
| This error is used to indicate an issue with an argument to special typing-related functions. |
| |
| For example, `typing.NewType` is a handy special form for creating types that are distinct from a base type. |
| |
| |
| ```python |
| from typing import * |
| |
| # Invalid argument to `NewType`: the first arg must match the name. |
| Mismatch = NewType("Wrong Name", int) |
| |
| # Invalid argument to `isinstance`: `NewType`s cannot be used in `isinstance`. |
| UserId = NewType("UserId", int) |
| if isinstance(1, UserId): |
| ... |
| ``` |
| |
| ## invalid-cast |
| |
| Default severity: `ignore` |
| |
| This warning is raised when `typing.cast` or `typing_extensions.cast` converts |
| between types that Pyrefly can prove have disjoint runtime classes. The check |
| applies to class instances and objects, literals, `None`, tuples, `TypedDict`, |
| and unions containing only supported types. Literals and structural containers |
| are compared using their nominal runtime classes. This means that a |
| `TypedDict` overlaps with `dict`, and tuple element types do not by themselves |
| make two tuples disjoint. Generic arguments similarly do not make two class |
| instances disjoint, so casting `list[int]` to `list[str]` does not produce this |
| warning. Type variables, protocols, `Never`, callables, and other types without |
| a sufficiently precise nominal runtime class are not reported. Two open nominal |
| classes are not considered disjoint unless their disjoint-base representatives |
| conflict, because a multiple-inheritance subclass could satisfy both. |
| |
| ```python |
| from typing import cast |
| |
| x: int = 1 |
| cast(str, x) # Cast from `int` to `str` is invalid because the types are disjoint [invalid-cast] |
| ``` |
| |
| ## invalid-decorator |
| |
| Default severity: `warn` |
| |
| This error indicates that a method-only decorator (`@final`, `@override`, etc.) was applied to a top-level function. Such usage is harmless at runtime and is sometimes intentional, so the default severity is `warn`. |
| |
| ```python |
| from typing import final |
| @final |
| def f() -> None: |
| pass |
| ``` |
| |
| Decorator misuse that violates the typing spec — for example, `@dataclass` on a |
| `Protocol`, `@disjoint_base` on a `TypedDict` or `Protocol`, |
| `@runtime_checkable` on a non-`Protocol` class, or `@disjoint_base` on a |
| function — is reported under `bad-class-definition` or |
| `bad-function-definition` instead, both of which default to `error`. |
| |
| ## invalid-inheritance |
| |
| An error caused by incorrect inheritance in a class or type definition. |
| This can pop up in quite a few cases: |
| - Trying to subclass something that isn't a class. |
| - Subclassing a type that does not support it, such as a `NewType` or a `Final` class. |
| - Attempting to mix `Protocol`s with non-`Protocol` base classes. |
| - Trying to make a generic enum. |
| - Trying to give a `TypedDict` a metaclass. |
| |
| And so on! |
| |
| ## invalid-literal |
| |
| `typing.Literal` only allows a [limited set](https://typing.python.org/en/latest/spec/literal.html#legal-parameters-for-literal-at-type-check-time) of types as parameters. |
| Attempting to use `Literal` with anything else is an error. |
| |
| ```python |
| from typing import Literal |
| |
| # These are legal |
| Literal[1] |
| Literal['a', 'b', 'c'] |
| # This is not |
| class A: |
| ... |
| Literal[A()] |
| ``` |
| |
| ## invalid-overload |
| |
| The `@overload` decorator requires that the decorated function has at least two overloaded signatures and a base implementation. |
| |
| ```python |
| from typing import * |
| |
| @overload |
| def no_base(x: int) -> None: |
| pass |
| |
| @overload |
| def no_base(x: str) -> int: |
| pass |
| ``` |
| |
| ```python |
| @overload |
| def just_one(x: int) -> None: |
| pass |
| |
| def just_one(x: str) -> None: |
| ... |
| ``` |
| |
| ## invalid-param-spec |
| |
| This error is reported when `typing.ParamSpec` is defined incorrectly or misused. For example: |
| |
| ```python |
| from typing import * |
| |
| P = ParamSpec("Name Must Match!") |
| |
| P1 = ParamSpec("P1") |
| P2 = ParamSpec("P2") |
| |
| def f(x, *args: P1.args, **kwargs: P2.kwargs) -> None: |
| pass |
| ``` |
| |
| Here, `P1.args` and `P2.kwargs` can't be used together; `*args` and `**kwargs` must come from the same `ParamSpec`. |
| |
| ## invalid-pattern |
| |
| This error is reported when a pattern is invalid at runtime. For example, enum members are values, |
| so they must be matched as value patterns (without `()`), not class patterns: |
| |
| ```python |
| from enum import Enum |
| |
| class Color(Enum): |
| RED = "red" |
| |
| def describe(color: Color) -> None: |
| match color: |
| case Color.RED(): # Invalid pattern: use `Color.RED` (without parentheses) |
| pass |
| ``` |
| |
| ## invalid-self-type |
| |
| This error occurs when `Self` is used in a context where it is not allowed. |
| |
| For example, `Self` is not allowed inside a `TypedDict`, so the following code |
| errors: |
| ```python |
| from typing import Optional, Self, TypedDict |
| |
| class TD(TypedDict): |
| x: Optional[Self] # error: `Self` is not allowed in a `TypedDict` |
| ``` |
| |
| ## invalid-sentinel |
| |
| An error caused by incorrect definition of a Sentinel. A few examples: |
| |
| ```python |
| from typing_extensions import Sentinel |
| |
| # First argument passed to sentinel constructor isn't a string literal |
| my_str: str = "MISSING" |
| A = Sentinel(my_str) |
| |
| # Invalid arguments passed to sentinel constructor |
| MISSING = Sentinel("MISSING", non_existent="") |
| ``` |
| |
| ## invalid-super-call |
| |
| `super()` has [a few restrictions](https://docs.python.org/3/library/functions.html#super) on how it is called. |
| |
| `super()` can be called without arguments, but only when used inside a method of a class: |
| |
| ```python |
| class Legal(Base1, Base2): |
| def f(self) -> None: |
| super().f() |
| |
| def illegal(arg: SomeType) -> None: |
| super().f() |
| ``` |
| |
| When the function is called with two arguments, like `super(T, x)`, then `T` must be a type, and the second argument is either an object where `isinstance(x, T)` is true |
| or a type where `issubclass(x, T)` is true. |
| |
| ## invalid-syntax |
| |
| This error covers syntactical edge cases that are not flagged by the parser. |
| |
| For example: |
| |
| ```python |
| x: list[int] = [0, 2, 3] |
| x[0]: int = 1 |
| ``` |
| |
| It's not a parse error for an assignment to have an annotation, but it is forbidden by the type checker to annotate assignment to a subscript like `x[0]`. |
| |
| ## invalid-type-alias |
| |
| An error related to the definition or usage of a `typing.TypeAlias`. Many of these cases are covered by [`invalid-annotation`](#invalid-annotation), so this error |
| specifically handles illegal type alias values: |
| |
| ```python |
| from typing import TypeAlias |
| x = 2 |
| Bad: TypeAlias = x |
| ``` |
| |
| ## invalid-type-checking-constant |
| |
| A module-level `TYPE_CHECKING` constant that is not typed as `bool`. Type checkers treat |
| `TYPE_CHECKING` as `True`, while at runtime it is `False`, so a user-defined one must be a `bool` |
| (conventionally `TYPE_CHECKING = False`). |
| |
| ```python |
| TYPE_CHECKING: str = "" # error: not a bool |
| ``` |
| |
| ## invalid-type-var |
| |
| An error caused by incorrect usage or definition of a TypeVar. A few examples: |
| |
| ```python |
| from typing import TypeVar |
| # Old-style TypeVars must be assigned to a matching variable. |
| Wrong = TypeVar("Name") |
| |
| # PEP 695-style TypeVars can be constrained, but there must be at least two: |
| def only_one_constraint[T: (int,)](x: T) -> T: |
| ... |
| |
| # It's also illegal to mix the two styles together. |
| T = TypeVar("T") |
| def mixed[S](a: S, b: T) -> None: |
| ... |
| ``` |
| |
| ## invalid-type-var-tuple |
| |
| An error caused by incorrect usage or definition of a TypeVarTuple. |
| |
| TypeVarTuple has similar error cases to [TypeVar](#invalid-type-var), but also a few of its own. For example: |
| |
| ```python |
| from typing import TypeVarTuple |
| |
| Ts = TypeVarTuple("Ts") |
| |
| # TypeVarTuples must always be unpacked: |
| bad: tuple[Ts] = (...) |
| good: tuple[*Ts] = (...) |
| |
| # Only one TypeVarTuple is allowed in a list of type arguments: |
| def two_tups[*Xs, *Ys](xs: tuple[*Xs], ys: tuple[*Ys]) -> None: |
| ... |
| ``` |
| |
| ## invalid-variance |
| |
| An error caused by a type variable being used in a position incompatible with its declared variance. |
| |
| For example, a covariant type variable cannot be used in a contravariant position (such as a method parameter), and a contravariant type variable cannot be used in a covariant position (such as a return type). |
| |
| ```python |
| from typing import TypeVar, Generic |
| |
| T_co = TypeVar("T_co", covariant=True) |
| T_contra = TypeVar("T_contra", contravariant=True) |
| |
| class BadCovariant(Generic[T_co]): |
| # Error: covariant type variable used in contravariant position |
| def set_value(self, value: T_co) -> None: ... |
| |
| class BadContravariant(Generic[T_contra]): |
| # Error: contravariant type variable used in covariant position |
| def get_value(self) -> T_contra: ... |
| ``` |
| |
| ## invalid-yield |
| |
| This error arises when `yield` is used in a way that is not allowed. For example: |
| |
| ```python |
| from typing import Generator |
| |
| for _ in range(1, 10): |
| yield "can't yield outside of a function!" |
| |
| def bad_yield_from() -> Generator[int, None, None]: |
| # `yield from` can only be used with iterables. |
| yield from 1 |
| ``` |
| |
| ## misplaced-ignore |
| |
| Default severity: `warn` |
| |
| A file-level `# pyrefly: ignore-errors` (or `# pyrefly: ignore-errors[code]`) directive |
| only takes effect when it appears at the top of the file, before any code. Placed after |
| the first line of code it is silently inert — it suppresses nothing — so Pyrefly flags it. |
| Move the directive to the top of the file, or use a line-level `# pyrefly: ignore[code]` |
| to suppress a single line. |
| |
| ```python |
| import os |
| |
| # pyrefly: ignore-errors # inert here — flagged as misplaced-ignore |
| x: int = "not an int" # this error is still reported |
| ``` |
| |
| ## missing-argument |
| |
| An error caused by calling a function without all the required arguments. |
| |
| ```python |
| def takes_two(x: int, y: int) -> int: |
| return x + y |
| |
| takes_two(1) |
| ``` |
| |
| ## missing-attribute |
| |
| This error is raised when attempting to access an attribute that does not exist on the given object or module. |
| |
| In the case of modules, attempting to import an nonexistent name will raise [`missing-module-attribute](#missing-module-attribute) instead. |
| |
| ```python |
| import os |
| from os import bacarat # missing-module-attribute |
| os.jongleur() # missing-attribute |
| ``` |
| |
| Note that objects with type `Any` will never raise this error. |
| |
| ## missing-attribute-patch-target |
| |
| Default severity: `warn` |
| |
| The attribute targeted by a `unittest.mock.patch` call does not exist. Example: |
| |
| ```python |
| from unittest import mock |
| @mock.patch("dep.nonexistent_attr") # missing-attribute-patch-target |
| def f(): ... |
| ``` |
| |
| This is a sub-kind of [missing-attribute](#missing-attribute): suppressing `missing-attribute` also suppresses this error. |
| |
| ## missing-import |
| |
| A module could not be found. |
| |
| The error message will include which paths were searched, such as the site package paths. |
| You may be missing a dependency, or you may need to inform Pyrefly where the module lives. See [Configuration](configuration.mdx) for further information. |
| |
| Even when no configuration file is present, this diagnostic is shown as a warning in the IDE. |
| |
| ## missing-module-attribute |
| |
| Arises when attempting to import a name that does not exist from a module. |
| |
| This is distinct from [`missing-import`](#missing-import), which is used when the module being imported does not exist, and [`missing-attribute`](#missing-attribute), when access attributes of the module. |
| |
| ```python |
| import this_does_not_exist # missing-import |
| import os.bacarat # missing-import |
| from os import joker # missing-module-attribute |
| os.perkeo # missing-attribute |
| ``` |
| |
| In this example, `os.bacarat` is treated as a module name, so failing to find it results in an `missing-import`. |
| `from os import joker` does not tell us if `joker` is a module, class, function, etc., so it is treated as the more general `missing-module-attribute`. |
| |
| ## missing-override-decorator |
| |
| Default severity: `ignore` |
| |
| A method overrides a parent class method but does not have the `@override` decorator. We do not emit this error for dunder methods that are inherited from `object` (e.g., `__repr__`, `__eq__`, `__str__`), but we do emit it for dunder methods inherited from other classes. |
| |
| This error supports strict override enforcement as specified in the [typing spec](https://typing.python.org/en/latest/spec/class-compat.html#strict-enforcement-per-project). When enabled, it requires all overriding methods to be explicitly marked with `@typing.override`. |
| |
| ```python |
| from typing import override |
| |
| class Base: |
| def foo(self) -> None: ... |
| def __len__(self) -> int: ... |
| |
| class Derived(Base): |
| def foo(self) -> None: ... # missing-override-decorator |
| |
| def __len__(self) -> int: ... # missing-override-decorator (inherited from Base, not object) |
| |
| def __repr__(self) -> str: ... # OK (inherited from object) |
| |
| @override |
| def foo(self) -> None: ... # OK |
| ``` |
| |
| To enable strict override enforcement, set the severity to `error` in your configuration: |
| |
| ```toml |
| [tool.pyrefly] |
| errors = { missing-override-decorator = "error" } |
| ``` |
| |
| ## missing-source |
| |
| Default severity: `ignore` |
| |
| Pyrefly was able to find a stubs package but no corresponding source package. For example, this can |
| happen if you install the `types-requests` package but forget to install `requests`. |
| |
| ## missing-source-for-stubs |
| |
| Pyrefly has bundled stubs for a package, but no corresponding source package was found. |
| |
| ## missing-super-call |
| |
| Default severity: `ignore` |
| |
| A constructor-like method overrides a parent class method but does not call `super()`. |
| |
| ```python |
| class Base: |
| def __init__(self) -> None: |
| self.value: int = 1 |
| |
| class Child(Base): |
| def __init__(self) -> None: # missing-super-call |
| pass |
| ``` |
| |
| To enable this check, set the severity to `error` in your configuration: |
| |
| ```toml |
| [tool.pyrefly] |
| errors = { missing-super-call = "error" } |
| ``` |
| |
| ## name-mismatch |
| |
| Default severity: `warn` |
| |
| This warning indicates that the first string argument to a functional type definition does not |
| match the name it is assigned to. |
| |
| ```python |
| from collections import namedtuple |
| from enum import Enum |
| |
| RepoDetails = namedtuple("repo_details", ["source_dir", "age"]) |
| DviState = Enum("_dvistate", "pre outer inpage") |
| ``` |
| |
| ## no-access |
| |
| The `no-access` error indicates that an attribute exists, but it cannot be used in this way. |
| |
| For example, classes do not have access to their instances' attributes: |
| |
| ```python |
| class Ex: |
| def __init__(self) -> None: |
| self.meaning: int = 42 |
| |
| del Ex.meaning # no-access |
| ``` |
| |
| ## no-any-return |
| |
| Default severity: `ignore` |
| |
| Umbrella error code for cases where a returned expression is `Any` in a function declared to return a concrete type other than `object`. Enabling this catches places where `Any` silently bypasses the function's declared return type. Returning `Any` as `object` is allowed because every runtime value is an object, so this does not weaken the return type's guarantee. |
| |
| Concrete diagnostics are emitted under one of the more specific sub-kinds: |
| - [`no-any-return-explicit`](#no-any-return-explicit) |
| - [`no-any-return-implicit`](#no-any-return-implicit) |
| |
| ## no-any-return-explicit |
| |
| Default severity: `ignore` |
| |
| The returned expression has type `Any`, but the function is declared to return a concrete type other than `object`. The returned `Any` originates from an explicit annotation or other introduction into the type system. |
| |
| This is a sub-kind of [no-any-return](#no-any-return): suppressing `no-any-return` also suppresses this error. |
| |
| ```python |
| from typing import Any |
| |
| def get_port(config: dict[str, Any]) -> int: |
| return config["port"] # no-any-return-explicit: Returning Any from function declared to return "int" |
| |
| # Fix: validate the value at the boundary where untyped data enters typed code. |
| def get_typed_port(config: dict[str, object]) -> int: |
| port = config["port"] |
| if isinstance(port, int): |
| return port |
| raise ValueError("port must be an int") |
| ``` |
| |
| ## no-any-return-implicit |
| |
| Default severity: `ignore` |
| |
| The returned expression was inferred as `Any` (e.g., from an untyped import or missing stub), but the function's return annotation is a concrete type other than `object`. This usually indicates that adding type information at an untyped boundary would restore type precision. |
| |
| This is a sub-kind of [no-any-return](#no-any-return): suppressing `no-any-return` also suppresses this error. |
| |
| ```python |
| def get_port(config) -> int: |
| return config["port"] # no-any-return-implicit: Returning implicit Any from function declared to return "int" |
| |
| # Fix: annotate the parameter and validate the value before returning it. |
| def get_typed_port(config: dict[str, object]) -> int: |
| port = config["port"] |
| if isinstance(port, int): |
| return port |
| raise ValueError("port must be an int") |
| ``` |
| |
| ## no-matching-overload |
| |
| This error is similar to the other bad function call errors, but specifically for cases where a function decorated with `@overload` is called with arguments that do not match any of the overloaded variations. |
| |
| For example, neither of the signatures of `f` can take an argument of type `float`: |
| |
| ```python |
| from typing import overload |
| |
| @overload |
| def f(x: int) -> int: |
| ... |
| |
| @overload |
| def f(x: str) -> str: |
| ... |
| |
| def f(x: int | str) -> int | str: |
| return x |
| |
| f(1.0) |
| ``` |
| |
| ## non-convergent-recursion |
| |
| Default severity: `warn` |
| |
| Some Python code has type analysis that is recursive. Consider, for example: |
| ``` |
| def f(): return g() |
| def g(): return [f()] |
| ``` |
| Here, return-type inference for `f` depends on the return type of `g`, and vice versa |
| so we get a cycle. Similarly, in |
| ``` |
| x = 1 |
| while some_condition(): |
| x = [x] |
| ``` |
| the type of `x` at the end of the loop body depends on the type of `x` at the beginning, |
| and this forms a cycle (and in this case the type doesn't converge to any closed form; |
| the true type is a recursive type but Pyrefly will not infer an anonymous recursive type). |
| |
| Many more cycles are possible, sometimes involving harder-to-visualize problems like |
| classes whose type parameters have constraints that are recursive, or class fields |
| whose inferred types depend on one another. |
| |
| Pyrefly will attempt to resolve all such recursion using a *fixpoint*, where we |
| repeatedly analyze the related entities in type inference. But because the results |
| do not always converge (like `x = [x]` above), we have to limit the number of |
| iterations, which means sometimes we cannot ensure that the inferred result |
| is correct. |
| |
| In these cases, Pyrefly will produce a `non-convergent-recursion` error that |
| warns you our fixpoint did not converge, and tells you the result we inferred |
| based on the last iteration. In some cases, the error message will recommend |
| that by adding more annotations you may be able to help Pyrefly determine the |
| correct type. |
| |
| If you're filing a bug report for this error, set the environment variable |
| `PYREFLY_FIXPOINT_DETAILS=1` when running Pyrefly and include additional |
| diagnostic information in the issue that will help the Pyrefly team root cause |
| the non-convergence. |
| |
| ## non-exhaustive-match |
| |
| Default severity: `warn` |
| |
| Pyrefly warns when a `match` statement may fall through without matching a case. By |
| default, this check applies to closed subject types such as enums, literal unions, |
| `None`, `bool`, and non-subclassable classes. Set |
| [`check-all-matches`](./configuration.mdx#check-all-matches) to check open-domain |
| subjects as well. Add the missing cases or a default arm. |
| |
| ```python |
| from enum import Enum |
| |
| class Color(Enum): |
| RED = "red" |
| BLUE = "blue" |
| |
| def describe(color: Color) -> str: |
| match color: # non-exhaustive-match |
| case Color.RED: |
| return "danger" |
| ``` |
| |
| ## not-a-type |
| |
| This indicates an attempt to use something that isn't a type where a type is expected. |
| In most cases, a more specific error kind is used. |
| |
| You may see this error around incorrect type aliases: |
| |
| ```python |
| class A: |
| ... |
| # Not an alias, just a string! |
| X = "A" |
| x: X = ... # X is not a type alias, so this is illegal |
| ``` |
| |
| ## not-async |
| |
| `not-async` is reported when attempting to `await` on something that is not |
| awaitable. This may indicate that a function should have been marked `async` but |
| wasn't. |
| |
| ```python |
| def some_func() -> None: |
| ... |
| |
| await some_func() # Expression is not awaitable [not-async] |
| ``` |
| |
| This will also arise if the context manager used in an `async with` statement |
| has `__aenter__` and `__aexit__` methods that are not marked `async`. |
| |
| The fix is to use an `async` function in the `await`. This may mean making the |
| function `async` or finding an existing `async` function to use instead. |
| |
| ## not-callable |
| |
| A straightforward error: something that is not a function was used as if it were a function. |
| |
| One interesting place this error may occur is with decorators: |
| |
| ```python |
| x = 1 |
| |
| @x # not-callable |
| def foo() -> None: |
| ... |
| ``` |
| |
| ## not-iterable |
| |
| This is most likely to be seen in a `for` loop: |
| |
| ```python |
| x = 1 # Or some other value |
| for val in x: # not-iterable |
| ... |
| ``` |
| |
| ## not-required-key-access |
| |
| Default severity: `ignore` |
| |
| This warning indicates that a [`TypedDict`](https://typing.python.org/en/latest/spec/typeddict.html) |
| key marked `NotRequired` (or inherited from a `total=False` TypedDict) is being accessed without |
| first ensuring that the key is present. Even if the value type is non-optional, the key itself may |
| not exist at runtime, so Pyrefly encourages guarding the access with an `in` check or a `.get()` |
| call. |
| |
| ```python |
| from typing import NotRequired, TypedDict |
| |
| class Movie(TypedDict): |
| title: str |
| year: NotRequired[int] |
| |
| def describe(movie: Movie) -> int: |
| return movie["year"] # not-required-key-access |
| |
| def safe_describe(movie: Movie) -> int: |
| if "year" in movie: |
| return movie["year"] # OK: key presence established |
| raise ValueError("Missing year") |
| ``` |
| |
| ## open-unpacking |
| |
| Default severity: `ignore` |
| |
| This error is reported on an attempt to unpack an |
| [open](https://typing.python.org/en/latest/spec/glossary.html#term-open) TypedDict that potentially |
| has items incompatible with the TypedDict it is being unpacked into. |
| |
| Example: |
| ```python |
| from typing import TypedDict |
| |
| class OpenTypedDict(TypedDict): |
| x: int |
| |
| class UnpackingTarget(TypedDict): |
| x: int |
| y: str |
| |
| def f(o: OpenTypedDict) -> UnpackingTarget: |
| # Error: `o` could be an instance of a subclass of `OpenTypedDict` with an |
| # item `y` with an incompatible type. |
| return {"y": "", **o} |
| ``` |
| |
| To fix this error, close the open TypedDict to indicate it does not contain any unknown items: |
| ```python |
| class OpenTypedDict(TypedDict, closed=True): ... |
| ``` |
| |
| Note: In Python versions before 3.15, import `TypedDict` from `typing_extensions` rather than |
| `typing` to use the `closed` feature. |
| |
| ## parse-error |
| |
| An error related to parsing or syntax. This covers a variety of cases, such as function calls with duplicate keyword args, some poorly defined functions, and so on. |
| |
| ## potential-bad-keyword-argument |
| |
| A potential conflict between an explicit keyword argument and a NotRequired TypedDict field. The field may be absent at runtime, so the conflict is not guaranteed. This is a separate error code from `bad-keyword-argument` to allow users to opt-in to this stricter check. |
| |
| ```python |
| from typing import TypedDict, NotRequired |
| |
| class Options(TypedDict, total=False): |
| name: str |
| |
| def f(name: str, **kwargs) -> None: ... |
| |
| opts: Options = {} |
| # Potential conflict: if 'name' is in opts at runtime, this will crash |
| f(name="test", **opts) # E: Multiple values for argument `name` |
| ``` |
| |
| ## protocol-implicitly-defined-attribute |
| |
| Protocols must declare the attributes they require directly in the class body. Assigning to a new `self` attribute inside a protocol method introduces a member that implementations of the protocol would never be required to provide. |
| |
| Add an annotated attribute (or property) to the protocol, or remove the assignment. |
| |
| ```python |
| from typing import Protocol |
| |
| class Template(Protocol): |
| name: str |
| |
| def method(self) -> None: |
| self.temp: list[int] = [] # protocol-implicitly-defined-attribute |
| ``` |
| |
| ## pytorch-efficiency-lint-cuda-call |
| |
| Default severity: `ignore` |
| |
| Calling `.cuda()` on a `torch.Tensor` hard-codes the target device to CUDA. Use `.to(device)` instead so your code works on any accelerator (CUDA, XPU, MPS, etc.). |
| |
| This is a sub-kind of [pytorch-efficiency-lints](#pytorch-efficiency-lints): enabling `pytorch-efficiency-lints` also enables this error. |
| |
| ```python |
| import torch |
| |
| def f(x: torch.Tensor) -> None: |
| y = x.cuda() # pytorch-efficiency-lint-cuda-call |
| # Fix: y = x.to(device) |
| ``` |
| |
| ## pytorch-efficiency-lint-item-call |
| |
| Default severity: `ignore` |
| |
| Calling `.item()` on a `torch.Tensor` forces GPU-to-CPU synchronization, blocking the training loop until all pending GPU operations complete. This can reduce GPU utilization from over 90% to under 50%. Prefer `tensor[0]` for scalar tensors, accumulate values on the GPU with `torch.sum()`, or defer `.item()` to outside the training loop. |
| |
| This is a sub-kind of [pytorch-efficiency-lints](#pytorch-efficiency-lints): enabling `pytorch-efficiency-lints` also enables this error. |
| |
| ```python |
| import torch |
| |
| def f(x: torch.Tensor) -> None: |
| v = x.item() # pytorch-efficiency-lint-item-call |
| ``` |
| |
| ## pytorch-efficiency-lint-print-tensor |
| |
| Default severity: `ignore` |
| |
| Passing a `torch.Tensor` to `print()` triggers `Tensor.__repr__()`, which forces GPU-to-CPU synchronization and blocks until all pending GPU operations complete. Use `print(tensor.shape)` to inspect metadata without synchronizing, or guard with `if DEBUG: print(tensor)`. |
| |
| This is a sub-kind of [pytorch-efficiency-lints](#pytorch-efficiency-lints): enabling `pytorch-efficiency-lints` also enables this error. |
| |
| ```python |
| import torch |
| |
| def f(x: torch.Tensor) -> None: |
| print(x) # pytorch-efficiency-lint-print-tensor |
| # Fix: print(x.shape) or print(x.dtype) |
| ``` |
| |
| ## pytorch-efficiency-lint-redundant-to-call |
| |
| Default severity: `ignore` |
| |
| Calling `.to(device)` on a tensor returned by a factory function like `torch.zeros()` allocates the tensor on CPU first, then copies it to the target device. Pass `device=` directly to the factory function instead to avoid the redundant allocation and copy. |
| |
| This is a sub-kind of [pytorch-efficiency-lints](#pytorch-efficiency-lints): enabling `pytorch-efficiency-lints` also enables this error. |
| |
| ```python |
| import torch |
| |
| device = torch.device("cuda") |
| x = torch.zeros(3, 4).to(device) # pytorch-efficiency-lint-redundant-to-call |
| # Fix: x = torch.zeros(3, 4, device=device) |
| ``` |
| |
| ## pytorch-efficiency-lints |
| |
| Default severity: `ignore` |
| |
| Umbrella error code for PyTorch GPU performance anti-patterns. Suppressing or enabling `pytorch-efficiency-lints` cascades to every sub-kind — useful when you want to surface (or silence) all of them without listing them individually. |
| |
| All concrete diagnostics are emitted under one of the more specific sub-kinds: |
| |
| - [`pytorch-efficiency-lint-cuda-call`](#pytorch-efficiency-lint-cuda-call) |
| - [`pytorch-efficiency-lint-item-call`](#pytorch-efficiency-lint-item-call) |
| - [`pytorch-efficiency-lint-print-tensor`](#pytorch-efficiency-lint-print-tensor) |
| - [`pytorch-efficiency-lint-redundant-to-call`](#pytorch-efficiency-lint-redundant-to-call) |
| |
| ## read-only |
| |
| This error indicates that the attribute being accessed does exist but cannot be modified. |
| |
| For example, a `@property` with no setter cannot be assigned to: |
| |
| ```python |
| class Ex: |
| @property |
| def meaning(self) -> int: |
| return 42 |
| |
| x = Ex() |
| x.meaning = 0 |
| ``` |
| |
| ## redefinition |
| |
| Pyrefly reports this error when a name that already has an annotation in the current scope is annotated again with a different type. Re-annotating the same variable can lead to confusing types; prefer introducing a new name instead. |
| |
| ```python |
| def f(x: int) -> None: |
| x: str = str(x) # redefinition |
| ``` |
| |
| ## redundant-cast |
| |
| Default severity: `warn` |
| |
| This warning is raised when `typing.cast()` is used to cast a value to a type it is already compatible with. Such casts are unnecessary and can be removed to improve code clarity. |
| |
| ```python |
| import typing |
| |
| x: int = 42 |
| # This cast is redundant since x is already an int |
| y = typing.cast(int, x) # redundant-cast |
| |
| # This is a valid cast since we're casting from a more general type |
| obj: object = "hello" |
| s = typing.cast(str, obj) # No warning - this is a valid cast |
| ``` |
| |
| The redundant cast warning helps identify unnecessary type casts that don't provide any additional type safety benefits. |
| |
| ## redundant-condition |
| |
| Default severity: `warn` |
| |
| This error is used to indicate a type that's equivalent to True or False is used as a boolean condition (e.g. an uncalled function) |
| |
| ```python |
| def f() -> bool: |
| ... |
| |
| # This is likely a mistake, as it's likely that the function needs to be invoked. |
| if f: |
| ... |
| # This is likely a mistake, as it's equivalent to `if True`. |
| if "abc": |
| ... |
| ``` |
| |
| ## regex |
| |
| This error is raised when Pyrefly can statically detect an invalid regular expression pattern. |
| |
| ```python |
| import re |
| |
| re.compile("(") # missing ), unterminated subpattern [regex] |
| ``` |
| |
| ## reveal-type |
| |
| Default severity: `info` |
| |
| Pyrefly uses this diagnostic to communicate the output of the [`reveal_type`](https://typing.python.org/en/latest/spec/directives.html#reveal-type) function. |
| |
| `reveal_type` is a *directive* — it is always shown in CLI output regardless of the [`min-severity`](../configuration#min-severity) threshold, and is never subject to suppression or baseline exclusion. To hide it, set `reveal-type = "ignore"` in the [`errors`](../configuration#errors) table. |
| |
| ## string-as-iterable |
| |
| Default severity: `ignore` |
| |
| This warning is raised when a string is passed to a parameter expecting an `Iterable[str]` or |
| `Sequence[str]`. While `str` is technically iterable, it iterates by individual characters, which |
| is often not what you intended. |
| |
| ```python |
| from typing import Iterable |
| |
| def takes_items(xs: Iterable[str]) -> None: |
| ... |
| |
| takes_items("hello") # Passing `str` treats it as an iterable of characters |
| ``` |
| |
| ## unannotated-attribute |
| |
| Default severity: `ignore` |
| |
| Deprecated: this error code has been renamed to [implicit-any-attribute](#implicit-any-attribute). The old name is still accepted in suppression comments and configuration for backwards compatibility. |
| |
| ## unannotated-parameter |
| |
| Default severity: `ignore` |
| |
| Deprecated: this error code has been renamed to [implicit-any-parameter](#implicit-any-parameter). The old name is still accepted in suppression comments and configuration for backwards compatibility. |
| |
| ## unannotated-protocol-member |
| |
| This error is raised when a protocol member is assigned a value in the class body without an explicit type annotation. Protocol members must have explicitly declared types so that implementations know exactly what type to provide. |
| |
| ```python |
| from typing import Protocol |
| |
| class MyProto(Protocol): |
| x = None # error: Protocol member `x` must have an explicit type annotation |
| |
| # Fixed version: |
| class MyProto(Protocol): |
| x: int | None = None |
| ``` |
| |
| ## unannotated-return |
| |
| Default severity: `ignore` |
| |
| This error is raised when a function is missing a return type annotation. This helps enforce fully-typed codebases by ensuring all functions declare their return types explicitly. To fix it, add a return type annotation to the function. |
| |
| ```python |
| def calculate_sum(x: int, y: int): # error: `calculate_sum` is missing a return annotation |
| return x + y |
| |
| # Fixed version: |
| def calculate_sum(x: int, y: int) -> int: |
| return x + y |
| ``` |
| |
| ## unbound-name |
| |
| Pyrefly found a conditional definition for the given name, so it may be undefined in some flows. For example: |
| |
| ```python |
| def f(check: bool): |
| if check: |
| x = 1 |
| return x # unbound-name |
| ``` |
| |
| Compare this with [unknown-name](#unknown-name), which is reported when no definition at all is found for a name. |
| |
| ## unexpected-keyword |
| |
| A function was called with an extra keyword argument. |
| |
| ```python |
| def two_args(a: int, b: int) -> int: |
| ... |
| |
| two_args(a=1, b=2, c=3) |
| ``` |
| |
| ## unexpected-positional-argument |
| |
| A positional argument was passed for a keyword-only parameter. |
| |
| ```python |
| def takes_kwonly(*, x: int) -> int: |
| ... |
| |
| takes_kwonly(1) # should be `takes_kwonly(x=1)`! |
| ``` |
| |
| ## unimported-directive |
| |
| Using a [type checker directive](https://typing.python.org/en/latest/spec/directives.html#type-checker-directives) without importing it from `typing` first will result in a runtime error. |
| |
| ```python |
| reveal_type(1) # error |
| assert_type(1, int) # error |
| ``` |
| |
| ## unknown-argument-type |
| |
| Default severity: `ignore` |
| |
| A call argument whose type is an implicit `Any` (unknown) is passed without the type checker being able to verify it against the parameter, hiding potential bugs. This mirrors pyright's `reportUnknownArgumentType`. |
| |
| ```python |
| def untyped(x): # unannotated: returns an implicit `Any` |
| return x |
| |
| def f(n: int) -> None: ... |
| |
| f(untyped(1)) # the argument's type is unknown [unknown-argument-type] |
| ``` |
| |
| ## unknown-attribute-type |
| |
| Default severity: `ignore` |
| |
| An unannotated attribute is assigned a value whose type is unknown, so the attribute is inferred as `Any`. Add an explicit attribute annotation or fix the right-hand side so Pyrefly can infer a concrete type. |
| |
| This is not a sub-kind of [implicit-any](#implicit-any): suppressing `implicit-any` does not suppress this error. |
| |
| ```python |
| def untyped(x): # unannotated: returns an implicit `Any` |
| return x |
| |
| class C: |
| def __init__(self) -> None: |
| self.x = untyped(1) # downstream users don't know what type `self.x` is |
| self.y: int = untyped(1) # downstream users treat `self.y` as `int` |
| ``` |
| |
| ## unknown-column |
| |
| Accessing a DataFrame column that does not exist in the inferred schema. |
| |
| |
| ## unknown-name |
| |
| A name is referenced but does not exist. |
| |
| Compare this with [unbound-name](#unbound-name), which is reported when the name is conditionally defined. |
| |
| Even when no configuration file is present, this diagnostic is shown as a warning in the IDE. |
| |
| ```python |
| def where() -> None: |
| # There is no spoon: unknown-name |
| global spoon |
| ``` |
| |
| ## unknown-variable-type |
| |
| Default severity: `ignore` |
| |
| A variable assigned a value of unknown type without an explicit annotation is inferred as `Any`, which hides type errors wherever it is used. Adding an annotation gives the variable a concrete type. |
| |
| ```python |
| def untyped(x): # unannotated: returns an implicit `Any` |
| return x |
| |
| y = untyped(1) # downstream users don't know what type `y` is |
| y2: int = untyped(1) # downstream users treat `y2` as `int` |
| ``` |
| |
| ## unnecessary-comparison |
| |
| Default severity: `warn` |
| |
| This warning is raised when an identity comparison (`is` or `is not`) is made between |
| literals whose comparison result is statically known. |
| |
| ```python |
| def test0() -> None: |
| # Different literals are always different objects |
| if 1 is 2: # unnecessary-comparison: always False |
| pass |
| # Same singletons are always the same object |
| if True is not False: # unnecessary-comparison: always True |
| pass |
| |
| class User: ... |
| class Admin(User): ... |
| |
| def test1(user: User) -> None: |
| # Comparing an instance to a class is always False |
| if user is Admin: # unnecessary-comparison: did you mean isinstance(user, Admin)? |
| pass |
| ``` |
| |
| This check is relatively conservative and only warns on limited cases where the comparison is highly likely to be redundant. |
| |
| ## unnecessary-type-conversion |
| |
| Default severity: `warn` |
| |
| This warning is raised when a builtin type constructor (`str`, `int`, `float`, `bool`, or `bytes`) is called on a value that is already of that type, making the conversion redundant. |
| |
| ```python |
| def f(x: str) -> None: |
| y = str(x) # unnecessary-type-conversion: `x` is already of type `str` |
| ``` |
| |
| ## unreachable |
| |
| Default severity: `warn` |
| |
| This error is raised when a `return` or `yield` can never be reached because it comes |
| after a statement that always exits the current flow, such as `return`, `raise`, `break`, or `continue`. |
| |
| ```python |
| def example(): |
| return 1 |
| return 2 # This `return` statement is unreachable [unreachable] |
| |
| def generator(): |
| return |
| yield 1 # This `yield` expression is unreachable [unreachable] |
| |
| def loop_example(): |
| while True: |
| break |
| return 1 # This `return` statement is unreachable [unreachable] |
| ``` |
| |
| Note that `yield` statements can follow other `yield` statements without error, since generators |
| can produce multiple values: |
| |
| ```python |
| def valid_generator(): |
| yield 1 |
| yield 2 # This is valid |
| ``` |
| |
| ## unreachable-match-case |
| |
| Default severity: `warn` |
| |
| This warning is raised when a `case` pattern in a `match` statement can never match |
| the subject because the subject's type is disjoint from the pattern's type. |
| |
| ```python |
| def example(x: list[int]) -> None: |
| match x: |
| case 1: # Case pattern can never match subject of type `list[int]` [unreachable-match-case] |
| pass |
| ``` |
| |
| This check currently covers value patterns (literals, `None`) and class patterns |
| on attributes, but does not flag top-level class patterns like `case SomeClass()`. |
| |
| ## unresolvable-dunder-all |
| |
| Default severity: `warn` |
| |
| This warning is raised when `__all__` is defined but its value cannot be |
| statically analyzed. This can happen when `__all__` is assigned a function call, |
| a variable, a list comprehension, or any expression that Pyrefly cannot resolve |
| at analysis time. |
| |
| When this occurs, Pyrefly falls back to inferring public exports from |
| module-level definitions (all names that do not start with an underscore). |
| |
| ```python |
| def generate_all(): |
| return ["x", "y"] |
| |
| __all__ = generate_all() |
| # `__all__` could not be statically analyzed [unresolvable-dunder-all] |
| ``` |
| |
| ## unsafe-overlap |
| |
| Protocols decorated with `@runtime_checkable` may be used in `isinstance` and `issubclass` checks, but the runtime will only checks that all the required attributes are present, without looking at their types. |
| |
| This error occurs when the object you're checking against the protocol has all the required attributes, but their types are not compatible. |
| |
| In the example below, `C` should not match with `P`, but the `isinstance` check will succeed at runtime. |
| |
| ```python |
| from typing import Protocol, runtime_checkable |
| |
| @runtime_checkable |
| class P(Protocol): |
| x: int |
| |
| class C: |
| x: str |
| |
| c = C() |
| if isinstance(c, P): |
| pass |
| ``` |
| |
| ## unsupported |
| |
| This error indicates that pyrefly does not currently support a typing feature. |
| |
| ## unsupported-delete |
| |
| This error occurs when attempting to `del` something that cannot be deleted. |
| |
| Besides obvious things like built-in values (you can't `del True`!), some object attributes are protected from deletion. |
| For example, read-only and required `TypedDict` fields cannot be deleted. |
| |
| ## unsupported-dynamic-base |
| |
| Default severity: `ignore` |
| |
| This error is raised for dynamic class definitions created with `type()` when |
| the base classes are not statically known class literals. |
| |
| ```python |
| class Base: ... |
| |
| def factory(base: type[Base]) -> type: |
| return type("Dynamic", (base,), {}) |
| # Base class `type[Base]` in `type()` call is not a statically known class [unsupported-dynamic-base] |
| ``` |
| |
| ## unsupported-operation |
| |
| This error arises when attempting to perform an operation between values of two incompatible types. |
| |
| ```python |
| x = 1 + "oops" # `+` is not supported between `int` and `str` [unsupported-operation] |
| ``` |
| |
| ## untyped-class-decorator |
| |
| Default severity: `ignore` |
| |
| A class decorator whose own type is `Any` may modify the class in unexpected ways. |
| |
| ```python |
| from typing import Any |
| |
| my_decorator: Any = lambda cls: cls |
| |
| @my_decorator # `my_decorator` is `Any`, so the type of `A` is lost [untyped-class-decorator] |
| class A: ... |
| ``` |
| |
| ## untyped-function-decorator |
| |
| Default severity: `ignore` |
| |
| A function or method decorator whose own type is `Any` obscures the decorated function's type. |
| |
| ```python |
| from typing import Any |
| |
| my_decorator: Any = lambda f: f |
| |
| @my_decorator # `my_decorator` is `Any`, so the type of `g` is lost [untyped-function-decorator] |
| def g() -> int: |
| return 1 |
| ``` |
| |
| ## untyped-import |
| |
| Default severity: `warn` |
| |
| Type information for some third-party libraries is shipped in a stubs package separate from the |
| library's source code. This error is emitted when we detect that a library is being used without |
| the recommended stubs package being installed. |
| |
| ## unused-call-result |
| |
| Default severity: `ignore` |
| |
| This rule is disabled by default and must be explicitly enabled. Once enabled, it reports when the |
| result of a call expression is discarded and the result type is informative (not `None`, `Any`, or |
| `Never`). This is often a sign of a mistake, such as forgetting to use the return value. |
| |
| ```python |
| def combine(a: list[int], b: list[int]) -> list[int]: |
| return a + b |
| |
| items = [1, 2, 3] |
| combine(items, [4, 5]) # warning: result is discarded |
| print(items) # ok: print returns None |
| x = combine(items, [4, 5]) # ok: result is used |
| ``` |
| |
| ## unused-coroutine |
| |
| If the result of an async function call is not awaited or used, we will raise an error. |
| |
| ```python |
| async def foo(): |
| return 1 |
| |
| async def bar(): |
| foo() # error |
| await foo() # ok |
| x = foo() # ok |
| ``` |
| |
| ## unused-ignore |
| |
| Default severity: `ignore` |
| |
| This error is raised when a `# pyrefly: ignore` comment is not used to suppress an error, and can be safely removed. |
| |
| ## unused-type-ignore |
| |
| Default severity: `ignore` |
| |
| This error is raised when a `# type: ignore` comment is not used to suppress any error, and can be safely removed. This rule is distinct from `unused-ignore` so that projects using multiple type checkers can leave `# type: ignore` comments for other tools (e.g. mypy) without pyrefly flagging them. Enable this rule if your project uses pyrefly exclusively. |
| |
| ## useless-overload-body |
| |
| Default severity: `warn` |
| |
| This warning is raised when an `@overload` function contains executable body logic. |
| Overload bodies are never executed at runtime, so only placeholder bodies like `pass`, `...`, |
| a docstring-only body, `raise NotImplementedError(...)`, or `return NotImplemented` are useful. |
| |
| ```python |
| from typing import overload |
| |
| @overload |
| def parse(x: int) -> int: |
| return x + 1 # warning: executable logic in an overload body |
| ``` |
| |
| ## variance-mismatch |
| |
| Default severity: `warn` |
| |
| The inferred variance of a type variable does not match its declared variance. This warning is raised for protocols where the way a type variable is used implies a different variance than what was declared. For example, if a protocol only uses `T` in covariant positions but `T` is declared as invariant, this warning suggests declaring `T` as covariant. |
| |
| ```python |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar("T") |
| |
| class A(Protocol[T]): # variance-mismatch: Type variable `T` in class `A` is declared as invariant, but could be covariant based on its usage |
| def f(self) -> T: ... |
| ``` |