github facebook/pyrefly 1.2.0-dev.3
Pyrefly v1.2.0-dev.3

pre-release4 hours ago

Release date: July 24, 2026

About dev releases
Dev releases (versions like X.Y.Z-dev.N) are non-stable snapshots cut periodically from trunk. They give early adopters a chance to try in-progress features and surface issues before the next stable release, but they don't carry the same stability or compatibility guarantees as a stable release — don't pin production projects to a dev version.

Pyrefly v1.2.0-dev.3 bundles 250 commits from 29 contributors.


✨ New & Improved

Type Checking

  • TypedDict classes now synthesize __required_keys__ and __optional_keys__ attributes during attribute lookup, so protocols requiring these attributes (like those in langgraph) work correctly.
  • Attrs fields can now use the @<field>.converter decorator (added in attrs 26.2.0) to define converters, and the converter's input type is correctly used for the __init__ parameter.
  • attrs.converters.pipe now types the __init__ parameter as the first converter's input type instead of Any, so invalid constructor arguments are caught.
  • attrs.converters.default_if_none now types the __init__ parameter as <field type> | None instead of Any, matching the converter's runtime behavior.
  • Generic function converters in attrs fields (like copy.deepcopy) now solve their type variables against the field's declared type, so the __init__ parameter is correctly typed.
  • Bare generic class converters in attrs fields now infer their element type from the field annotation, so converter=tuple on a Sequence[int] field types the parameter as Iterable[int].
  • Polars DataFrames constructed from dictionary literals now infer a schema tracking each column's name and type, so later column access is type-checked.
  • Polars .select(), .drop(), .rename(), and .with_columns() operations now preserve and transform the DataFrame schema, keeping column information through method chains.
  • Polars .filter(), .sort(), and .fill_null() operations now preserve the DataFrame schema unchanged, since they only affect rows.
  • Pandas DataFrames are now tracked with partial schemas (marked with ... in display), so known columns are type-checked while avoiding false positives from dynamic column operations.
  • functools.partial now type-checks bound arguments and synthesizes a precise residual signature for the remaining parameters, catching errors at construction and call sites.
  • functools.partial over generic functions now preserves type variables, so bound arguments can solve them and the residual stays generic.
  • functools.partial over overloaded functions now resolves to the matching overload branches, so later calls still benefit from overload resolution.
  • functools.partial over class constructors and bound methods now works correctly, checking arguments and producing the expected instance type.
  • functools.partial results now expose .func, .args, and .keywords attributes and narrow correctly in isinstance checks.
  • functools.partial over **Unpack[TypedDict] splats now expands the TypedDict fields and checks the residual parameters correctly.
  • Enum .value on an enum type (not a specific member) now infers as the union of member literal values instead of widening to the mixin type.
  • Positional class patterns now narrow matched attributes correctly, resolving the attribute name from __match_args__ at solve time.
  • Sequence pattern element captures now read from the narrowed subject type, so sibling element constraints refine the captured types.
  • isinstance checks on facets (like tuple elements or union members) now filter the parent union to matching members.
  • Unpacking variadic tuples with fixed prefix/suffix now types starred captures precisely instead of smearing the fixed elements into the star.

Language Server

  • Misplaced file-level # pyrefly: ignore-errors directives (placed after code) now emit a misplaced-ignore warning pointing users to move them to the top.
  • Module auto-import completions no longer suggest duplicate entries for modules already imported with regular import statements.
  • Overloaded class methods now associate the implementation's docstring with the overload chain, so hover shows documentation at call sites.
  • Import alias names (import x as y, from x import y as z) are now classified as definition contexts, so completions don't suggest module paths for them.
  • Completions for import aliases now preserve the original exported name and generate correct from model import MyModel as MyModelAlias statements.
  • Inlay hints now debounce server-side (default 150ms) to prevent width jitter while typing, settling only when editing pauses.
  • Keyword-argument hover now resolves parameter types from the defining module and falls back to the selected call signature when refinement is unavailable.
  • Hover on and/or operators now returns the enclosing boolean expression range for better context.
  • Renaming a Protocol class now renames the class symbol instead of incorrectly renaming __init__.
  • Selection ranges (textDocument/selectionRange) now follow AST nesting from expression to statement to scope to document, with support for #region/#endregion markers.
  • #region/#endregion folding markers are now recognized (including nested regions and spaced # region), independently of comment-section folding.
  • TSP type queries (getComputedType, getDeclaredType, getExpectedType) now resolve correctly for files that aren't open in the editor.
  • Pyrefly client settings (pyrefly.*) are now ignored when running as a type server (TSP mode), so stale extension settings don't interfere with Pylance.

Error Reporting

  • A new unknown-column error kind flags reads of DataFrame columns that don't exist in the known schema.
  • A new column-type-mismatch error kind flags Polars column literals whose elements cannot share one dtype.
  • A new implicit-reexport error kind (default: ignore) flags imports of names that are only available through plain import/from ... import (not explicit re-exports).
  • A new invalid-type-checking-constant error kind flags user-defined TYPE_CHECKING constants that aren't assignable to bool.
  • A new missing-super-call error kind (default: ignore) flags __init__, __new__, or __init_subclass__ methods that override a non-object parent without calling the parent method.
  • A new empty-body error kind (default: ignore) flags ellipsis-only function bodies where None isn't assignable to the annotated return type.
  • A new unused-call-result error kind (default: ignore) flags discarded function/method call results whose type is informative (not None, Any, or Never).
  • A new invalid-abstract-method error kind (default: ignore) flags @abstractmethod decorators in non-abstract classes.
  • Error summary output is now sorted by count then name, so ties break deterministically and the output is stable across runs.
  • The full-text-with-github output format now emits readable diagnostics followed by GitHub workflow commands, so source locations remain visible in raw logs.

Tensor Shape Typing

  • Many improvements. Still early alpha state.

Polars & Pandas Support

  • Polars DataFrame construction from dictionary literals now infers a complete schema with column names and types.
  • Polars column reads by string literal now error on unknown columns, catching typos before runtime.
  • Polars list subscripts (like df[["a", "b"]]) now narrow the schema to the selected columns in the requested order.
  • Polars .select() narrows the schema to the named columns in the order given.
  • Polars .drop() narrows the schema by removing the named columns while preserving the original order.
  • Polars .rename() relabels columns in the schema, handling swaps correctly and falling back on ambiguous cases.
  • Polars .with_columns() adds or overwrites columns in the schema, appending new names and preserving untouched columns.
  • Polars selector strings (like "*" and regex patterns) are now recognized, so .select("*") and .drop("^col.*$") fall back instead of erroring.
  • Pandas DataFrames are tracked with partial schemas (kind: Pandas, completeness: Partial), so known columns are checked without false positives from dynamic operations.
  • Polars column literals now type each column from its first element and error on incompatible mixes, matching Polars runtime behavior.

Configuration & Tooling

  • pyrefly coverage check --public-only now keeps public re-exports from excluded files, so symbols are only excluded when all public re-export locations are excluded.
  • pyrefly coverage commands now accept a [coverage] config table with includes and excludes that override project globs, so coverage can be measured on a different file set.
  • pyrefly coverage check --fail-under now always prints findings regardless of whether the threshold is met, so users see what's missing even when passing.
  • pyrefly coverage now excludes privately-named classes (like _Foo) unless exported via __all__, consistent with other symbol kinds.
  • Config files without a [tool.pyrefly] section are now only considered root markers, so pyproject.toml files don't shadow parent configs unless they contain Pyrefly settings.
  • TOML parse errors now point at the offending value instead of the start of the file, so users can find the mistake quickly.
  • The --project-excludes flag now takes precedence over coverage.excludes, and positional FILES... args also override coverage globs.
  • Module handles for files without a config now apply the fallback_search_path, so __unknown__ files count under --public-only.

🐛 Bug fixes

We closed 48 bug issues this release 👏

  • #4071: Fixed incorrect bad-override error when a child class generic over a TypeVarTuple overrides a parent method with the same *args: *Ts signature.
  • #3745: TypedDict classes now synthesize __required_keys__ and __optional_keys__ attributes, so protocols requiring these attributes (like those in langgraph) no longer fail structural checks.
  • #4109: Module auto-import completions no longer suggest duplicate entries for modules already imported with regular import statements (like import json).
  • #3619: Overloaded class methods now associate the implementation's docstring with the overload chain's first declaration, so hover shows documentation at call sites.
  • #3437: Fixed stack overflow when checking recursive enum classes by removing the recursive Flag subtype check and moving Flag detection downstream.
  • #3354: Fixed panic with fuzzed NamedTuple code by adding a distinct binding key for invalid assignment targets, preventing collisions with synthesized functional NamedTuple bindings.
  • #3240: Constructor hover types are now resolved before rendering, preventing internal @NNN variables from leaking into tooltips.
  • #1323: enumerate() over tuple literals now preserves element types instead of widening to a generic tuple, so for i, x in enumerate(("a", "b")) types x as Literal["a"] | Literal["b"].
  • #3305: #region/#endregion folding markers are now recognized (including nested regions and spaced # region), independently of comment-section folding.
  • #4155: Generic function converters in attrs fields (like copy.deepcopy) now solve their type variables against the field's declared type, so the __init__ parameter is correctly typed instead of leaking a bare TypeVar.
  • And more! #4123, #4125, #4127, #4128, #3330, #3638, #3546, #4107, #3329, #2495, #4034, #4082, #3900, #3435, #3701, #3056, #633, #3857, #3814, #3154, #3085, #2817, #1770, #1689, #1510, #964, #1866, #1084, #4164, #3821, #3727, #3669, #3368, #2504, #2235, #1681, #1663, #1373

Thank-you to all our contributors who found these bugs and reported them! Did you know this is one of the most helpful contributions you can make to an open-source project? If you find any bugs in Pyrefly we want to know about them! Please open a bug report issue here.


📦 Upgrade

pip install --upgrade pyrefly==1.2.0-dev.3

How to safely upgrade your codebase

Upgrading the version of Pyrefly you're using or a third-party library you depend on can reveal new type errors in your code. Fixing them all at once is often unrealistic. We've written scripts to help you temporarily silence them. After upgrading, follow these steps:

  1. pyrefly check --suppress-errors
  2. Run your code formatter of choice
  3. pyrefly check --remove-unused-ignores
  4. Repeat until you achieve a clean formatting run and a clean type check.

This will add # pyrefly: ignore comments to your code, enabling you to silence errors and return to fix them later. This can make the process of upgrading a large codebase much more manageable.

Read more about error suppressions in the Pyrefly documentation.


🖊️ Contributors this release

@stroxler, @shobhitmehro, generatedunixname2066905484085733, @yangdanny97, @asukaminato0721, @kinto0, @anushamukka-dev, @samwgoldman, @rchen152, @NathanTempest, @jorenham, @aodihis, @tobyh-canva, @brosenfeld, @nitishagar, @goutamadwant, @grievejia, @roian6, @paranoa233, @kz357, @teerthsharma, generatedunixname949130641157030, @coclique, @SarahMKosowsky, @markselby9, @oprypin, @knQzx, @alexander-beedie, @ak4-sh


Please note: These release notes summarize major updates and features. For brevity, not all individual commits are listed.

Don't miss a new pyrefly release

NewReleases is sending notifications on new releases.