Neil Mitchell: How Pyrefly Checks Python at Scale

A tour of Python's type system and the incremental architecture behind a fast, open-source checker and language server.

NMNeil Mitchell登壇者 · Meta
チャンネル
Jane Street
公開日
2025年10月29日
動画
55:03
読了時間
9 分
スライド
9
原文
英語
YouTube で開く
スクロールすると動画は右下に小さくなって再生を続けます。字幕は閲覧言語に合わせて切り替わります。
閲覧言語2 言語で読めます
この記事はまだ日本語に翻訳されていないため、英語の原文を表示しています。
要約

30 秒でわかる

Neil Mitchell explains why Meta built Pyrefly after Pyre and how Python's evolving type system shapes its design. He covers file-level incremental checking, bindings, inference, circular imports, memory management and compatibility with existing code.

  1. 1Lessons from PyreMeta's earlier checker faced language-integration, architecture, and open-source adoption challenges that shaped the successor.
  2. 2Incrementality at file levelFile-level invalidation simplifies the dependency graph while requiring fast per-file rechecking.
  3. 3Bindings before answersThe checker converts control flow and name resolution into key-value bindings, then solves their types.
  4. 4Import cycles without full rechecksThe checker first assumes neighboring interfaces are unchanged, propagating changes and falling back to a full cycle when necessary.
  5. 5Inference is a user-experience choiceAn audience suggestion explores delaying the choice of a concrete list type with bounded variables.

ポイント

  1. Python's type system must describe existing dynamic code, making inference and compatibility important design choices.
  2. Pyrefly uses a file-level incremental build architecture to check large codebases and respond quickly to edits.
  3. A bindings stage separates control-flow and name resolution from subsequent type computations.
  4. Compact exported interfaces and eviction reduce memory, while deferred answers handle cyclic dependencies.
  5. Incremental checking can optimistically reuse interfaces in import cycles and fall back to invalidating the cycle when needed.
  6. The talk describes Pyrefly as an evolving alpha project, with open-source use and compatibility as priorities.
全文

チャプターごとに整えた全文を読む

文字起こしのみ
01

Python types and Pyrefly

Mitchell introduces static checking, inference and editor support. He explains Meta's scale and the history from Pyre to Pyrefly.

Neil Mitchell, a programmer at Meta who previously worked on Buck2, introduces Pyrefly, an open-source Python type checker and language server. Python annotations can specify, for example, an integer parameter and a string return value, but the interpreter does not generally enforce them as static guarantees.

Once annotations exist, runtime tools can check values as the program executes, while static checkers examine code before execution. Python has several tools in both categories. Its type language has grown rich, with generics, literal types, narrowing, higher-order functions, and other features supported by a specification and conformance tests.

Pyrefly is written in Rust for speed, parallelism, and cross-platform support. The team previously built Pyre. A key goal for Pyrefly is inference: programmers provide useful hints, and the checker infers much of the rest so Python code can retain its ordinary style.

In a sandbox example, Pyrefly displays inferred types in the editor, infers a list's element type, reports a string passed where an integer is annotated, and supplies completion for a known string result. Mitchell notes that a type error can be stricter than a Python runtime error: the particular string argument in his example would actually run.

Pyrefly sandbox and inferred types
スライド 1 / 9Pyrefly sandbox and inferred typesThe example shows inferred annotations, a type error, and editor completion.

The scale motivating this work is substantial. Mitchell says Instagram's server contains over 20 million lines of Python and about 3,300 Meta developers work on Python on a given day. At this scale, type checking helps people change code without breaking unrelated behavior.

Meta started Pyre in 2017 after encountering scale and performance problems with the available checker, mypy. Pyre was written in OCaml and shared ideas with the company's previous work adding types to PHP and JavaScript. It became important to Meta's development workflow.

Mitchell says OCaml was not an ideal fit for Meta's environment at the time, citing Windows support, a difficult multiprocessing and shared-memory approach to parallelism, and barriers for outside contributors. He emphasizes that some issues reflected Meta's own investment choices and that OCaml was not the sole reason for Pyre's problems.

Problems with the earlier Pyre checker
スライド 2 / 9Problems with the earlier Pyre checkerThe slide separates OCaml integration challenges from Pyre’s architectural and adoption issues.

Pyre also had structural constraints: its abstract-interpreter design extensively desugared code, lacked a conventional symbol table and scope resolution, began as a command-line tool, and did not initially focus on open-source users. Retrofitting those facilities after adoption was difficult.

Two developers began prototyping a successor around August 2024, exploring constraint solving, more traditional subtyping approaches, and abstract interpretation. They chose Rust as a practical combination of speed, memory safety, portability, and growing adoption in the Python tooling community.

The prototype became a project in October 2024. The team chose the name Pyrefly after discovering a collision with an existing Python binding named pyre2. An alpha was released at PyCon; Mitchell describes the checker as still under development but ready to try.

02

The shape of Python's type system

Types were added to an established dynamic language. Literals, generics, protocols, overloads and narrowing reflect that history.

Python's type system was grafted onto a huge amount of existing code, rather than designed alongside the language from the beginning. It keeps growing to describe previously untyped patterns. Types can catch problems in rarely exercised exception handlers and provide a useful check on generated code.

A standard and conformance tests provide common ground, but implementations still choose how much to infer. More inference can actually reject more programs, Mitchell observes, because a checker that infers less may approximate unknown information more broadly.

Python supports class types and literal types, such as a particular file-mode string or the integer 42. Annotations may be evaluated at runtime, and imports needed only for types can have a cost. Quoted annotations and explicit type-alias syntax address some complications, though older forms can make it hard to distinguish a value from a type alias.

Aliases and forward references
スライド 3 / 9Aliases and forward referencesPython permits type aliases and quoted annotations, making the distinction between types and values subtle.

The gradual type Any represents an unknown static type and is assignable in either direction. Mitchell points out that unions involving Any can behave less intuitively than simply collapsing everything to Any. Never and NoReturn describe unreachable or uninhabited results.

For unannotated variables, Pyrefly generally tries to infer something more informative than Any; unannotated function parameters, in the approach he describes, default to Any. Python generics support type variables, constraints, bounds, and variance, including newer syntax that can infer variance.

Protocols support structural subtyping: an object with the required close method can satisfy a closing protocol without explicitly inheriting from it. Overloads give different signatures for different inputs; the implementation must be compatible with those signatures, but the checker does not prove that each declared behavioral relationship is true.

Typing callable parameters
スライド 4 / 9Typing callable parametersThe example uses Callable and a parameter specification for positional and keyword arguments.

Higher-order typing must handle Python's positional and keyword arguments. Parameter specifications can describe a generic bundle of those arguments. Flow-sensitive narrowing also refines types: after testing a string-or-None value for truth, a branch can treat it as a string.

Typing existing built-ins is another challenge. Typeshed supplies descriptions, but some runtime APIs do not fit a perfectly tidy type model. Mitchell mentions dictionary and mutable-mapping compatibility as an example where pragmatic treatment helps existing programs work.

03

A build system for type checking

File-level incrementality, memory eviction and support for cycles drive the architecture. Each file passes through six stages.

Pyrefly's design goals include time and memory efficiency at Instagram scale, command-line and IDE support, integration with build systems, and a codebase open-source contributors can modify. Circular imports are a required case, not an exceptional one, because large Python codebases commonly contain them.

Drawing on his build-system experience, Mitchell chose file-level dependencies and invalidation. Finer granularity would add complexity, while a large codebase already has plenty of files. A keystroke invalidates a whole file, so rechecking that file must typically take only a few milliseconds; unused data must also be evicted.

Pyrefly architecture
スライド 5 / 9Pyrefly architectureA build-system layer coordinates six stages from source code to the exported interface.

A build-system layer manages dependencies, invalidation and concurrency while each file passes through six stages: code, AST, exports, bindings, answers, and interface. It obtains source from disk or an editor buffer and parses it using Ruff's error-correcting Python parser, which can return an AST even for incomplete code.

The exports stage determines what names each module provides, including names reached through star imports. Circular star imports can require a least fixed point. These results also establish dependencies between modules for the build graph.

04

Bindings, answers and inference

Bindings capture control flow and name resolution. Deferred answers, contextual types and fresh variables help solve Python's typing problems.

The bindings stage turns statements into key-value relationships describing how to compute the type of each identified occurrence. It resolves sequencing, flow control and name uses before the later type calculations. A use can point to a definition or to an import in another module.

Bindings and control flow
スライド 6 / 9Bindings and control flowThe example translates definitions, narrowing, and a branch join into keyed bindings.

At a branch join, a phi operation combines the possible definitions, often as a union. A test such as isinstance supplies narrowing on a branch. The result is a compact intermediate language for Python type checking rather than repeated reasoning over the original control-flow structure.

An audience question raises the walrus assignment expression. Mitchell calls it a complication because it can define names inside expressions, but explains that the binding pass handles it by linearizing relevant boolean expressions and creating appropriate bindings. Loops and assignment expressions make this part of the implementation more intricate.

The answers stage evaluates the binding relationships and computes larger expression types from subexpression types. Cycles can arise in imports and return-type inference. Deferred computations, or thunks, and fresh variables help represent answers that are not yet known.

The interface retains the types of exported names, a smaller subset of the answers. To save memory, Pyrefly can discard ASTs, bindings, and answers after producing an interface, while retaining more information for files open in an IDE so navigation and other editor features work well.

Mitchell illustrates inference with lists. A list containing literal 1 is generally inferred as list[int], not list[Literal[1]], because mutable lists are invariant and a literal-only list could prevent later useful operations. A mixed list can be inferred with a union; contextual annotations can tell the checker to retain a literal type instead.

For an empty list followed by a generic add operation, Pyrefly starts with a fresh unknown element type. The generic function is instantiated with another fresh variable; constraints from the list and the value 1 connect the variables and determine an integer element type. The checker looks to the next use to constrain the list, otherwise falling back to Any.

Inferring an empty list
スライド 7 / 9Inferring an empty listFresh variables and constraints connect the empty list to an integer passed to a generic function.
05

Cycles, memory and extensibility

Compact interfaces and an optimistic cycle algorithm reduce work. Mitchell discusses performance, built-in integrations, recursion and open-source development.

Cross-module queries can use a compact interface once available or force an answer when a cycle means the interface has not yet been completed. Only exported answers are visible to other modules. This separation permits lazy computation during cycles without making private answers part of the interface.

Incremental checking with cycles is tricky: collapsing a large import cycle into one node could force tens of thousands of modules to be rechecked. If A changes, Pyrefly first checks it assuming B and C's old interfaces. If A's exported interface is unchanged, the other files need not be invalidated.

Incremental checking with cycles
スライド 8 / 9Incremental checking with cyclesThe algorithm first assumes neighboring interfaces are unchanged and falls back when propagation requires it.

If the interface changes, the checker propagates to the affected modules and may revisit A. To avoid endless iteration, it falls back to invalidating the entire cycle after revisiting work or reaching a depth limit. The common case can be much faster, at the cost of occasional repeated work or less parallelism.

Mitchell describes exports, bindings, and answers as increasingly expensive stages, each roughly an order of magnitude more costly than the previous one in his profile. Threads and thread pools matter, but parallelism had not yet been the most important optimization. Transaction-like coordination allows editor queries and updates to proceed concurrently.

Instead of immediately offering an external plugin API for a rapidly evolving type system, Pyrefly incorporates extensions directly. Mitchell cites built-in Pydantic support, ongoing Django work, and native Buck integration; other build-system support was also being explored.

Recursive answers
スライド 9 / 9Recursive answersA thunk and fresh variable allow the checker to represent and later bind a recursive result.

For recursion, the checker can place a thunk where a cycle may occur. If evaluation encounters the same computation again on its call stack, it inserts a fresh variable and later constrains that variable to the computed result. The approach does not solve every case, but the same variable mechanism is useful for generics, inference and recursion.

Pyrefly is MIT-licensed, accepts contributions, and has an issue tracker and Discord. Mitchell says the team deliberately prioritizes open-source users because the wider Python ecosystem benefits Meta too. At the time of the talk, he describes the project as alpha software with bugs and more than 100 contributors.

06

Questions on inference and compatibility

The audience explores bounded variables, cyclic interfaces and subtype constraints. Mitchell describes migration experience with PyTorch.

In questions, an audience member suggests retaining a bounded variable as the most general type for a list rather than immediately choosing list[int]. Mitchell agrees this might defer a choice between, for example, a union and a shared supertype until later use, and says the team could explore it; multiple inheritance complicates the lattice.

Another question probes whether using answers during a cyclic calculation conflicts with interface-based invalidation. Mitchell explains that cross-module queries can only access exported answers, and completed interfaces contain the same exported information. Deferred answers are needed while the interfaces are still being computed.

Asked about constraint solving, Mitchell says most constraints are subtype relationships, such as requiring an argument's type to be a subtype of a parameter's type. Unions make those relationships more complicated, and adding intersection types can make subset checks resemble satisfiability solving.

On compatibility, Mitchell describes work moving PyTorch from mypy to Pyrefly: a few hundred type ignores were added and a comparable number removed. He says the checkers are broadly comparable but may accept different programs, including cases Pyrefly flags that another checker misses. Large projects can require more migration effort.

同じチャンネルの記事

スライド 6 枚15:33EN
JSJane Street· 2026年10月26日Figgie Strategy: Reading Hands, Winning Majorities and Pricing CardsBradley, Mike · Jane StreetJane Street traders Bradley and Mike analyze three games to show how private information, market competition and timing affect card prices.Decision MakingMathematicsFintech
1:48:29EN
JSJane Street· 2026年10月16日How AI Is Changing Programming: John Krazit and Ron MinskyRon Minsky, John Krazit · Jane StreetJane Street’s AI tooling lead and the Signals and Threads host examine agent infrastructure, security, code review and the changing experience of writing software.コーディングエージェントDeveloper productivity評価(Evals)+2
1:36:37EN
JSJane Street· 2026年9月23日Aaron Bauer on Teaching Software Engineering in the Age of AIRon Minsky, Aaron Bauer · Jane StreetHow learning goals, active practice and editor design shape engineering education when models can generate the code.AI in EducationSoftware engineeringEducation+2
スライド 9 枚1:05:45EN
JSJane Street· 2026年9月11日Cyrus Omar on Live Programming and a Computational Commons for Humans and AICyrus OmarA demonstration of Hazel shows how immediate feedback, structured collaboration and carefully scoped permissions could support people and AI agents working together.Programming LanguagesHuman-AI Collaboration開発ワークフローとテスト+2
1:25:00EN
JSJane Street· 2026年9月2日Eric Mannes on Turning Messy External Data Into Trading ResearchRon, Eric Mannes · Jane StreetFrom commodity futures and an Excel risk system to point-in-time datasets and AI, Eric Mannes explains the domain knowledge behind data engineering at Jane Street.Data quality & curation開発ワークフローとテストInvesting+2
スライド 7 枚1:16:29EN
JSJane Street· 2026年8月26日Grant Sanderson and Alok Puranik: Positional Encodings Through Group TheoryGrant Sanderson, Alok Puranik · 3Blue1BrownStarting with assumptions about attention, they derive matrix exponentials and connect rotations, decay and Jordan blocks to RoPE and ALiBi.Transformers & attentionMathematics機械学習の基礎+1