Skip to content

Commit b641ff4

Browse files
committed
Switch type checking from mypy to Pyrefly (#1399)
Replace mypy with Pyrefly via check/typecheck, migrate config, fix a few annotations, and update CI, deps, and docs. Keep check/mypy as a deprecated wrapper.
1 parent 7e1af8b commit b641ff4

25 files changed

Lines changed: 173 additions & 526 deletions

File tree

.github/problem-matchers/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,19 @@ license. The version of the files at the time they were copied was 2025.1.2.
2020
- [`pylint.json`](https://github.com/home-assistant/core/blob/dev/.github/workflows/matchers/pylint.json)
2121
- [`yamllint.json`](https://github.com/home-assistant/core/blob/dev/.github/workflows/matchers/yamllint.json)
2222

23-
The Mypy and Pytest problem matchers files originally came from the
23+
The Pytest problem matcher file originally came from the
2424
[gh-problem-matcher-wrap](https://github.com/liskin/gh-problem-matcher-wrap/tree/master/problem-matchers)
25-
repository (copied 2025-03-04, version 3.0.0), and were subsequently modified by
26-
Michael Hucka. The original JSON files are Copyright © 2020 Tomáš Janoušek and
25+
repository (copied 2025-03-04, version 3.0.0), and was subsequently modified by
26+
Michael Hucka. The original JSON file is Copyright © 2020 Tomáš Janoušek and
2727
made available under the terms of the MIT license.
2828

29-
- [`mypy.json`](https://github.com/liskin/gh-problem-matcher-wrap/blob/master/problem-matchers/mypy.json)
3029
- [`pytest.json`](https://github.com/liskin/gh-problem-matcher-wrap/blob/master/problem-matchers/pytest.json)
3130

31+
The typecheck problem matcher matches Pyrefly's text and GitHub Actions output
32+
formats used by `check/typecheck`.
33+
34+
- [`typecheck.json`](./typecheck.json)
35+
3236
The actionlint problem matcher JSON file came from the
3337
[actionlint](https://github.com/rhysd/actionlint/blob/v1.7.7/docs/usage.md)
3438
documentation (copied on 2025-02-12, version 1.7.7). Actionlint is made

.github/problem-matchers/mypy.json

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"problemMatcher": [
3+
{
4+
"owner": "typecheck",
5+
"severity": "error",
6+
"pattern": [
7+
{
8+
"regexp": "^(?:ERROR|WARN(?:ING)?)\\s+([^:]+):(\\d+):(\\d+)(?:-\\d+)?:\\s+(.*?)(?:\\s+\\[(\\S+)\\])?$",
9+
"file": 1,
10+
"line": 2,
11+
"column": 3,
12+
"message": 4,
13+
"code": 5
14+
}
15+
]
16+
}
17+
]
18+
}

.github/workflows/ci.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ env:
4646
# This is used by setup-python to check whether its cache needs updating.
4747
python-dep-files: >-
4848
dev_tools/requirements/envs/format.env.txt
49-
dev_tools/requirements/envs/mypy.env.txt
49+
dev_tools/requirements/envs/typecheck.env.txt
5050
dev_tools/requirements/envs/pylint.env.txt
5151
dev_tools/requirements/envs/pytest-extra.env.txt
5252
dev_tools/requirements/envs/pytest.env.txt
@@ -83,7 +83,7 @@ jobs:
8383
run: |
8484
pip install -r dev_tools/requirements/envs/format.env.txt
8585
pip install -r dev_tools/requirements/envs/pylint.env.txt
86-
pip install -r dev_tools/requirements/envs/mypy.env.txt
86+
pip install -r dev_tools/requirements/envs/typecheck.env.txt
8787
8888
- name: Check format
8989
run: |
@@ -97,8 +97,8 @@ jobs:
9797
9898
- name: Check type declarations
9999
run: |
100-
echo '::add-matcher::.github/problem-matchers/mypy.json'
101-
check/mypy
100+
echo '::add-matcher::.github/problem-matchers/typecheck.json'
101+
check/typecheck --output-format github
102102
103103
pytest:
104104
name: Unit tests

.pre-commit-config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ repos:
170170
files: \.(cc|h|cu|py)$
171171
stages: [pre-push]
172172

173+
- id: typecheck
174+
name: 'check/typecheck runs without errors'
175+
entry: check/typecheck
176+
language: script
177+
pass_filenames: false
178+
types: [python]
179+
stages: [pre-push]
180+
173181
- repo: https://github.com/Pierre-Sassoulas/copyright_notice_precommit
174182
rev: 'd9215b6b2a028d1614c92cf43a9fcff3b1dd889e' # frozen: 0.1.2
175183
hooks:

CONTRIBUTING.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,15 @@ for information about how to set up a local copy of the software for development
234234
### Type annotation conventions
235235
236236
Code should have [type annotations](https://www.python.org/dev/peps/pep-0484/). We use
237-
[mypy](http://mypy-lang.org/) to check that type annotations are correct, and the following script
238-
to run it:
237+
[Pyrefly](https://pyrefly.org/) to check that type annotations are correct, and the following
238+
script to run it:
239239
240240
```shell
241-
check/mypy
241+
check/typecheck
242242
```
243243
244+
(`check/mypy` remains as a deprecated wrapper that forwards to `check/typecheck`.)
245+
244246
### Linting and formatting
245247
246248
Code should meet common style standards for Python and be free of error-prone constructs. We use

check/all

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ done
100100
declare -a errors=()
101101
declare -a pylint_j=()
102102
declare -a pytest_n=()
103-
declare -a mypy_n=()
103+
declare -a typecheck_j=()
104104

105105
function run() {
106106
echo "~~~~ Running $* ~~~~"
@@ -112,11 +112,11 @@ function run() {
112112
cpus=$(python3 -c 'import psutil; print(psutil.cpu_count(logical=False))')
113113

114114
if [[ -z "${no_parallel}" ]]; then
115-
# Pylint & pytest can auto-detect the number of CPUs, but not mypy. To be
116-
# consistent, this uses the same explicit number for all of them.
115+
# Pylint & pytest can auto-detect the number of CPUs; pyrefly uses -j.
116+
# To be consistent, this uses the same explicit number for all of them.
117117
pylint_j=("-j" "${cpus}")
118118
pytest_n=("-n" "${cpus}")
119-
mypy_n=("-n" "${cpus}")
119+
typecheck_j=("-j" "${cpus}")
120120
fi
121121

122122
if [[ -n "${only_changed}" ]]; then
@@ -126,7 +126,7 @@ else
126126
run check/format-incremental "${rev[@]}" "${apply_arg[@]}" --all
127127
run check/pylint "${pylint_j[@]}" "${rev[@]}"
128128
fi
129-
run check/mypy "${mypy_n[@]}"
129+
run check/typecheck "${typecheck_j[@]}"
130130
run check/pytest-and-incremental-coverage "${pytest_n[@]}" "${rev[@]}"
131131
run check/shellcheck
132132
run check/nbformat

check/mypy

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,18 @@
1414
# limitations under the License.
1515

1616
################################################################################
17-
# Runs mypy on the repository starting from the root.
17+
# Deprecated wrapper that forwards to check/typecheck (Pyrefly).
1818
#
1919
# Usage:
2020
# check/mypy [--flags]
2121
################################################################################
2222

23+
echo "Warning: 'check/mypy' is deprecated." \
24+
"Please use 'check/typecheck' instead." >&2
25+
echo >&2
26+
2327
# Get the working directory to the repo root.
2428
cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" || exit 1
2529
cd "$(git rev-parse --show-toplevel)" || exit 1
2630

27-
echo -e -n "\033[31m"
28-
mypy "$@" .
29-
result=$?
30-
echo -e -n "\033[0m"
31-
32-
exit ${result}
31+
exec check/typecheck "$@"

check/shellcheck

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ required_shell_scripts=(
6262
check/pylint-changed-files
6363
check/pytest
6464
check/pytest-and-incremental-coverage
65+
check/typecheck
6566
)
6667

6768
scripts_not_found=$(comm -13 \

check/typecheck

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
################################################################################
17+
# Runs type checking on the repository using Pyrefly.
18+
#
19+
# Usage:
20+
# check/typecheck [--flags]
21+
################################################################################
22+
23+
# Get the working directory to the repo root.
24+
cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" || exit 1
25+
cd "$(git rev-parse --show-toplevel)" || exit 1
26+
27+
echo -e -n "\033[31m"
28+
pyrefly check "$@"
29+
result=$?
30+
echo -e -n "\033[0m"
31+
32+
exit ${result}

0 commit comments

Comments
 (0)