facebook / pyrefly
A fast type checker and language server for Python
AI Architecture Analysis
This repository is indexed by RepoMind. By analyzing facebook/pyrefly in our AI interface, you can instantly generate complete architecture diagrams, visualize control flows, and perform automated security audits across the entire codebase.
Our Agentic Context Augmented Generation (Agentic CAG) engine loads full source files into context on-demand, avoiding the fragmentation of traditional RAG systems. Ask questions about the architecture, dependencies, or specific features to see it in action.
Repository Overview (README excerpt)
Crawler viewPyrefly: A fast type checker and language server for Python with powerful IDE features Pyrefly is a type checker and language server for Python, which provides lightning-fast type checking along with IDE features such as code navigation, semantic highlighting, and code completion. It is available as a command-line tool and an extension for popular IDEs and editors such as VSCode, Neovim, Zed, and more. See the Pyrefly website for full documentation and how to add Pyrefly to your editor of choice. Currently under active development with known issues. Please open an issue if you find bugs. Getting Started • Try out pyrefly in your browser: Sandbox • Get the command-line tool: • Get the IDE Extension: IDE installation page Key Features: • Type Inference: Pyrefly infers types in most locations, apart from function parameters. It can infer types of variables and return types. • Flow Types: Pyrefly can understand your program's control flow to refine static types. • Incrementality: Pyrefly aims for large-scale incrementality at the module level, with optimized checking and parallelism. Getting Involved If you have questions or would like to report a bug, please create an issue. See our contributing guide for information on how to contribute to Pyrefly. Join our Discord to chat about Pyrefly and types. This is also where we hold biweekly office hours. Choices There are a number of choices when writing a Python type checker. We are taking inspiration from Pyre1, Pyright and MyPy. Some notable choices: • We infer types in most locations, apart from parameters to functions. We do infer types of variables and return types. As an example, would result in something equivalent to had you written . • We attempt to infer the type of to however it is used first, then fix it after. For example will infer that and then error on the final statement. • We use flow types which refine static types, e.g. will both know that has type , but also that the immediately next usage of will be aware the type is . • We aim for large-scale incrementality (at the module level) and optimized checking with parallelism, aiming to use the advantages of Rust to keep the code a bit simpler. • We expect large strongly connected components of modules, and do not attempt to take advantage of a DAG-shape in the source code. Code layout Pyrefly is split into a number of crates (mostly under ): • are general purpose utilities, which have nothing to do with Python or type checking. Examples include IO wrappers, locking, command line helpers etc. • are proc-macros for deriving traits such as and . • are Python utilities with no type-checking aspects, such as modelling modules or . • provides utilities for indexing values and caching computations that may depend on each other. • are the third-party typeshed stubs. • defines the Pyrefly configuration, along with support for reading Mypy/Pyright configuration. • defines the Pyrefly type along with operations on it. • defines the sandbox code that compiles to WASM. • itself is the type checker and everything else. Design There are many nuances of design that change on a regular basis. But the basic substrate on which the checker is built involves three steps: • Figure out what each module exports. That requires solving all statements transitively. • For each module in isolation, convert it to bindings, dealing with all statements and scope information (both static and flow). • Solve those bindings, which may require the solutions of bindings in other modules. If we encounter unknowable information (e.g. recursion) we use to insert placeholders which are filled in later. For each module, we solve the steps sequentially and completely. In particular, we do not try and solve a specific identifier first (like Roslyn or TypeScript), and do not use fine-grained incrementality (like Rust Analyzer using Salsa). Instead, we aim for raw performance and a simpler module-centric design - there's no need to solve a single binding in isolation if solving all bindings in a module is fast enough. Example of bindings Given the program: We might produce the bindings: • = • = • = • = • = Of note: • The keys are things like (the definition of something), (a usage of a thing) and (a statement we need to type check, but don't care about the result of). • In many cases the value of a key refers to other keys. • Some keys are imported from other modules, via keys and values. • In order to disambiguate identifiers we use the textual position at which they occur (in the example we've used , but in reality it's the byte offset in the file). Example of Given the program: We end up with the bindings: • = • = • = The expression is the join point of the two values, e.g. would be . We skip the distinction between and , since it is not necessary for this example. When solving we encounter recursion. Operationally: • We start solving . • That requires us to solve . • We solve to be • We start solving . But we are currently solving , so we invent a fresh (let's call it ) and return that. • We conclude that must be . • Since was introduced by we record that . We can take the upper reachable bound of that and conclude that . • We simplify to just .