Introduction
Python is widely used for building web applications, automation tools, APIs, data processing systems, and backend services. As a Python project grows, managing types becomes more important. A small mistake in a function argument, return value, or object structure can cause problems that are difficult to find during testing.
Static type checkers help developers identify these problems before running the application. Pytype is one such tool. It analyzes Python code, checks types, and identifies possible errors, even when developers have not added type annotations everywhere.
Pyrefly is another option for Python developers. It provides static type checking along with language server features that help developers understand code directly inside their editors.
If an existing project uses Pytype, moving to Pyrefly can improve the development workflow. However, both tools have different approaches to type analysis and configuration. Their results may not always be identical.
This article explains how to migrate an existing Python project from Pytype to Pyrefly, configure the new checker, review type errors, and integrate it into a development workflow.
Understanding Pytype and Pyrefly
Before starting the migration, it is useful to understand how these tools work.
Pytype analyzes Python code and identifies potential type errors. One of its useful features is type inference, which allows it to understand types even when developers have not explicitly added annotations.
Pyrefly is a Python type checker and language server developed by Meta. It provides type checking, code navigation, autocomplete, hover information, and editor diagnostics.
Both tools help improve code quality, but they are not direct replacements for each other in terms of configuration and behavior.
| Feature | Pytype | Pyrefly |
|---|
| Static type checking | Yes | Yes |
| Type inference | Yes | Yes |
| Command-line support | Yes | Yes |
| Editor integration | Requires editor integration | Includes language server support |
| Code navigation | Depends on additional tooling | Supported |
| Configuration | Pytype-specific configuration | TOML-based configuration |
| Type error reporting | Yes | Yes |
| Existing configuration migration | Tool-specific | Requires manual review for Pytype projects |
The main point to remember is that Pyrefly does not automatically convert every Pytype configuration setting. Developers should review existing settings and configure the new checker according to the project requirements.
Preparing the Existing Python Project
Do not uninstall Pytype immediately. First, prepare the project and record the results produced by the existing checker.
Consider a project with the following structure:
python-project/
│
├── src/
│ └── app/
│ ├── __init__.py
│ ├── models.py
│ ├── services.py
│ └── main.py
│
├── tests/
│ ├── test_models.py
│ └── test_services.py
│
├── requirements.txt
├── setup.cfg
└── pyproject.toml
Before migration, check the current Git status:
git status
Run the existing test suite:
python -m pytest
Then run Pytype:
pytype
Save the output so that you can compare it with the results from Pyrefly.
Also review the following project details:
- Python version used by the application.
- Existing Pytype configuration.
- Source code and test directories.
- Third-party packages and their type information.
- Existing type annotations.
- Ignored files and generated code.
This preparation helps identify whether a new error is caused by an actual problem in the application or a difference in how the two tools analyze Python code.
Installing Pyrefly Without Removing Pytype
Install Pyrefly in the existing development environment.
If the project uses pip, run:
python -m pip install pyrefly
If the project uses uv, run:
uv add --dev pyrefly
After installation, verify that the command is available:
pyrefly --version
Now execute the initial type check:
pyrefly check
At this stage, do not worry if Pyrefly reports errors that were not reported by Pytype. Different type checkers can make different decisions about type inference, missing annotations, imports, and dynamic Python features.
The purpose of this initial check is to understand how Pyrefly analyzes the existing project.
Creating the Pyrefly Configuration
Pyrefly supports two configuration locations:
pyrefly.toml in the project root.- A
[tool.pyrefly] section inside pyproject.toml.
For an existing Pytype project, creating a separate pyrefly.toml file is a good starting point.
Run the initialization command:
pyrefly init
This command creates or updates the Pyrefly configuration. It can also migrate supported configurations from tools such as mypy and Pyright. Pytype settings still need to be reviewed and transferred manually.
A basic configuration for the example project could look like this:
# pyrefly.toml
project-includes = [
"src/**/*.py",
"tests/**/*.py"
]
project-excludes = [
"**/__pycache__",
"**/.venv",
"**/generated"
]
search-path = [
"src"
]
python-version = "3.12"
Here is what each setting does:
project-includes specifies which Python files Pyrefly should check.project-excludes prevents unnecessary checking of generated files and virtual environments.search-path identifies the directories Pyrefly should use when resolving application imports.python-version specifies the Python version used for type analysis.
Change the Python version and directory paths according to your application.
One important point is that project-includes controls which files are checked, while search-path controls where imports are resolved from. These settings serve different purposes and should not be treated as interchangeable.
Updating Python Type Annotations
Moving to another type checker is a good opportunity to review existing type annotations.
Consider a service method that accepts user information:
def create_user(user):
name = user["name"]
email = user["email"]
return {
"name": name,
"email": email
}
The function works, but it does not clearly describe the expected input or output types.
A better approach is to use TypedDict and explicit return annotations.
from typing import TypedDict
class UserInput(TypedDict):
name: str
email: str
class UserResponse(TypedDict):
name: str
email: str
def create_user(user: UserInput) -> UserResponse:
return {
"name": user["name"],
"email": user["email"]
}
Now the expected structure of the input and output is clear.
If another developer passes an object without the required fields, the type checker can identify the problem during development.
This approach is particularly useful for:
- API request and response models.
- Database records.
- Configuration objects.
- Shared service methods.
- Data passed between application modules.
You do not need to rewrite every function during migration. Start with frequently used modules and important application boundaries.
Comparing Pytype and Pyrefly Results
After configuring Pyrefly, run both tools again:
pytype
pyrefly check --summarize-errors
Compare the results and investigate the differences.
For example, a function may pass Pytype but produce a diagnostic in Pyrefly because of an incomplete annotation or a different inference decision.
Use the following process when reviewing errors:
| Error type | What to check | Recommended action |
|---|
| Missing import | Import path and installed packages | Correct the package or search path |
| Incorrect argument type | Function arguments | Update the caller or annotation |
| Return type mismatch | Function return value | Correct the return value or annotation |
| Unknown object type | Missing type information | Add suitable annotations |
| Dictionary key issue | Expected dictionary structure | Consider using TypedDict |
| Third-party type issue | Package typing support | Review available type stubs |
Do not automatically suppress every diagnostic. First determine whether it identifies a genuine problem in the application.
Handling Existing Errors With a Baseline
Large applications may already contain hundreds of type-checking errors. Fixing all of them before adopting Pyrefly can delay the migration.
Pyrefly provides a baseline feature that records existing diagnostics. Developers can then focus on newly introduced problems.
Generate a baseline:
pyrefly check --baseline=pyrefly-baseline.json --update-baseline
Run the checker again:
pyrefly check --baseline=pyrefly-baseline.json
The baseline suppresses matching existing diagnostics in command-line output.
This is useful when introducing a new type checker into an established application because developers can improve existing code gradually without ignoring newly introduced errors.
Keep the baseline file in version control so that the entire team uses the same reference.
As existing problems are fixed, update the baseline and review the changes before committing them.
Integrating Pyrefly Into the CI/CD Pipeline
Running type checks only on a developer's machine is not enough for a shared application. The same checks should run when developers create pull requests or push changes.
For example, consider the following GitHub Actions workflow:
name: Python Quality Checks
on:
push:
branches:
- main
pull_request:
jobs:
quality-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyrefly pytest
- name: Run Pyrefly
run: pyrefly check
- name: Run tests
run: python -m pytest
This workflow performs two important checks.
First, Pyrefly analyzes the application and reports type-related problems. Second, pytest runs the test suite to verify application behavior.
Both checks are important because static type checking does not replace application testing.
For an existing project, configure the same baseline file in CI if you are using one. Also pin the Pyrefly version in your development dependencies to keep local and CI results consistent.
Advantages and Disadvantages of Pyrefly
Before completely replacing Pytype, consider the practical advantages and limitations.
| Advantages | Disadvantages |
|---|
| Provides static type checking | Existing Pytype configuration needs manual review |
| Includes language server functionality | Developers need to learn its configuration |
| Supports code navigation and editor diagnostics | Different type inference can produce different results |
| Supports incremental adoption through baselines | Existing suppressions may need changes |
| Integrates with command-line workflows | CI configuration needs updating |
Pyrefly can make the development experience more convenient, especially when developers want type information directly inside their editors.
However, switching tools does not automatically improve application performance or remove existing bugs. The benefits depend on the quality of the configuration and how developers use the reported diagnostics.
Common Mistakes During Migration
Removing Pytype Too Early
Removing Pytype immediately after installing Pyrefly makes it difficult to compare the results. Keep both tools available until the migration is complete.
Copying Existing Configuration Without Reviewing It
Pytype and Pyrefly use different configuration options. Copying settings from setup.cfg or another Pytype configuration file does not mean Pyrefly will understand them.
Review each setting and configure its equivalent only when one exists.
Ignoring Type Errors
Using ignore comments for every reported issue can hide actual problems. Fix incorrect annotations and function implementations wherever possible.
Checking Only the Main Application
Utility modules, tests, and shared libraries may also contain type errors. Include the appropriate project directories in the configuration.
Using Different Python Versions
If local development uses one Python version and CI uses another, type-checking results may differ. Keep the Python version consistent across environments.
Troubleshooting Common Problems
| Problem | Possible reason | Solution |
|---|
| Pyrefly cannot find a module | Incorrect search path | Review search-path |
| Too many type errors appear | Different inference behavior | Review diagnostics and use a baseline |
| Third-party imports show errors | Missing package or type information | Install dependencies and check type support |
| Some files are not checked | Incorrect include patterns | Review project-includes |
| Local and CI results differ | Different dependencies or configurations | Align both environments |
| Editor and CLI show different results | Different configuration discovery | Review project and editor configuration |
When an import problem occurs, use the following command to inspect the configuration selected for a particular file:
pyrefly dump-config src/app/services.py
This can help identify incorrect paths, Python version settings, and other configuration differences.
Best Practices for a Smooth Migration
A gradual migration is generally easier to manage than replacing the existing checker across the entire project at once.
Follow these practices:
- Start with a stable Git branch so that migration changes can be reviewed independently.
- Keep Pytype installed until Pyrefly produces acceptable results across the application.
- Pin the Pyrefly version in development dependencies to avoid unexpected changes.
- Add annotations to important models, service methods, and API boundaries first.
- Use a baseline to manage existing type errors instead of suppressing everything.
- Run type checking as part of pull-request validation.
- Review new diagnostics before merging code.
- Document the new configuration and commands so developers can use the same workflow.
- Remove Pytype dependencies and configuration only after confirming that the replacement process works.
These practices help maintain consistency across development machines and CI environments.
Summary
Migrating a Python project from Pytype to Pyrefly requires careful planning, configuration updates, and validation. Both tools provide static type checking, but their type inference, diagnostics, and configuration options are different.
Start by running Pytype and saving its existing results. Install Pyrefly alongside it, create a new configuration, and compare the diagnostics produced by both tools.
Use better type annotations to describe application data, introduce a baseline to manage existing errors, and integrate Pyrefly into the CI/CD pipeline.
Do not remove Pytype until the new checker has been tested across the important parts of the application.
A gradual migration helps developers adopt Pyrefly without making unnecessary changes to existing application code. It also provides an opportunity to improve type annotations and make Python projects easier to maintain.
Join the conversation! Your thoughts help the community grow.