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.

FeaturePytypePyrefly
Static type checkingYesYes
Type inferenceYesYes
Command-line supportYesYes
Editor integrationRequires editor integrationIncludes language server support
Code navigationDepends on additional toolingSupported
ConfigurationPytype-specific configurationTOML-based configuration
Type error reportingYesYes
Existing configuration migrationTool-specificRequires 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:

Code
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:

Bash
git status

Run the existing test suite:

Bash
python -m pytest

Then run Pytype:

Bash
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:

Bash
python -m pip install pyrefly

If the project uses uv, run:

Bash
uv add --dev pyrefly

After installation, verify that the command is available:

Bash
pyrefly --version

Now execute the initial type check:

Bash
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:

Bash
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:

PowerShell
# 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:

Python
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.

Python
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:

Bash
pytype
Bash
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 typeWhat to checkRecommended action
Missing importImport path and installed packagesCorrect the package or search path
Incorrect argument typeFunction argumentsUpdate the caller or annotation
Return type mismatchFunction return valueCorrect the return value or annotation
Unknown object typeMissing type informationAdd suitable annotations
Dictionary key issueExpected dictionary structureConsider using TypedDict
Third-party type issuePackage typing supportReview 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:

Bash
pyrefly check --baseline=pyrefly-baseline.json --update-baseline

Run the checker again:

Bash
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:

SQL
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.

AdvantagesDisadvantages
Provides static type checkingExisting Pytype configuration needs manual review
Includes language server functionalityDevelopers need to learn its configuration
Supports code navigation and editor diagnosticsDifferent type inference can produce different results
Supports incremental adoption through baselinesExisting suppressions may need changes
Integrates with command-line workflowsCI 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

ProblemPossible reasonSolution
Pyrefly cannot find a moduleIncorrect search pathReview search-path
Too many type errors appearDifferent inference behaviorReview diagnostics and use a baseline
Third-party imports show errorsMissing package or type informationInstall dependencies and check type support
Some files are not checkedIncorrect include patternsReview project-includes
Local and CI results differDifferent dependencies or configurationsAlign both environments
Editor and CLI show different resultsDifferent configuration discoveryReview project and editor configuration

When an import problem occurs, use the following command to inspect the configuration selected for a particular file:

Bash
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:

  1. Start with a stable Git branch so that migration changes can be reviewed independently.
  2. Keep Pytype installed until Pyrefly produces acceptable results across the application.
  3. Pin the Pyrefly version in development dependencies to avoid unexpected changes.
  4. Add annotations to important models, service methods, and API boundaries first.
  5. Use a baseline to manage existing type errors instead of suppressing everything.
  6. Run type checking as part of pull-request validation.
  7. Review new diagnostics before merging code.
  8. Document the new configuration and commands so developers can use the same workflow.
  9. 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.