Pyrefly v1.2.0-dev.3
Pre-releaseRelease date: July 24, 2026
About dev releases
Dev releases (versions likeX.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>.converterdecorator (added in attrs 26.2.0) to define converters, and the converter's input type is correctly used for the__init__parameter. attrs.converters.pipenow types the__init__parameter as the first converter's input type instead ofAny, so invalid constructor arguments are caught.attrs.converters.default_if_nonenow types the__init__parameter as<field type> | Noneinstead ofAny, 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=tupleon aSequence[int]field types the parameter asIterable[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.partialnow type-checks bound arguments and synthesizes a precise residual signature for the remaining parameters, catching errors at construction and call sites.functools.partialover generic functions now preserves type variables, so bound arguments can solve them and the residual stays generic.functools.partialover overloaded functions now resolves to the matching overload branches, so later calls still benefit from overload resolution.functools.partialover class constructors and bound methods now works correctly, checking arguments and producing the expected instance type.functools.partialresults now expose.func,.args, and.keywordsattributes and narrow correctly inisinstancechecks.functools.partialover**Unpack[TypedDict]splats now expands the TypedDict fields and checks the residual parameters correctly.- Enum
.valueon 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.
isinstancechecks 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-errorsdirectives (placed after code) now emit amisplaced-ignorewarning 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 MyModelAliasstatements. - 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/oroperators 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/#endregionmarkers. #region/#endregionfolding 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-columnerror kind flags reads of DataFrame columns that don't exist in the known schema. - A new
column-type-mismatcherror kind flags Polars column literals whose elements cannot share one dtype. - A new
implicit-reexporterror kind (default: ignore) flags imports of names that are only available through plainimport/from ... import(not explicit re-exports). - A new
invalid-type-checking-constanterror kind flags user-definedTYPE_CHECKINGconstants that aren't assignable tobool. - A new
missing-super-callerror kind (default: ignore) flags__init__,__new__, or__init_subclass__methods that override a non-object parent without calling the parent method. - A new
empty-bodyerror kind (default: ignore) flags ellipsis-only function bodies whereNoneisn't assignable to the annotated return type. - A new
unused-call-resulterror kind (default: ignore) flags discarded function/method call results whose type is informative (notNone,Any, orNever). - A new
invalid-abstract-methoderror kind (default: ignore) flags@abstractmethoddecorators 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-githuboutput 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
DataFrameconstruction 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-onlynow keeps public re-exports from excluded files, so symbols are only excluded when all public re-export locations are excluded.pyrefly coveragecommands now accept a[coverage]config table withincludesandexcludesthat override project globs, so coverage can be measured on a different file set.pyrefly coverage check --fail-undernow always prints findings regardless of whether the threshold is met, so users see what's missing even when passing.pyrefly coveragenow 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, sopyproject.tomlfiles 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-excludesflag now takes precedence overcoverage.excludes, and positionalFILES...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-overrideerror when a child class generic over aTypeVarTupleoverrides a parent method with the same*args: *Tssignature. - #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
@NNNvariables from leaking into tooltips. - #1323:
enumerate()over tuple literals now preserves element types instead of widening to a generic tuple, sofor i, x in enumerate(("a", "b"))typesxasLiteral["a"] | Literal["b"]. - #3305:
#region/#endregionfolding 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 bareTypeVar. - 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.3How 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:
pyrefly check --suppress-errors- Run your code formatter of choice
pyrefly check --remove-unused-ignores- 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.