Skip to content

Commit 77434bd

Browse files
committed
Enable mypy, part 1
Summary: This is the first PR to enable MyPy typechecker. There are few next steps, but this can land as-is as a standalone PR. Will keep enabling in smaller batches, via directory by directory. Also side note, pyre-ignore can be migrated to "# type: ignore" since pyre is backwards compatible with mypy.
1 parent 2600cc8 commit 77434bd

16 files changed

+137
-13
lines changed

.github/workflows/lint.yml

+6
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@ jobs:
2020
with:
2121
runner: linux.2xlarge
2222
docker-image: executorch-ubuntu-22.04-linter
23+
submodules: 'true'
2324
fetch-depth: 0
2425
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
2526
timeout: 90
2627
script: |
2728
# The generic Linux job chooses to use base env, not the one setup by the image
2829
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
2930
conda activate "${CONDA_ENV}"
31+
32+
# For mypy linting, we need to first install executorch first so that
33+
# it builds the python package information.
34+
BUILD_TOOL="cmake"
35+
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "${BUILD_TOOL}"
3036
3137
CACHE_DIRECTORY="/tmp/.lintbin"
3238
# Try to recover the cached binaries

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.hypothesis
22
buck-out/
3+
.mypy_cache/
34
buck2-bin/
45
cmake-out*
56
.DS_Store

.lintrunner.toml

+46
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,49 @@ command = [
285285
'--',
286286
'@{{PATHSFILE}}',
287287
]
288+
289+
[[linter]]
290+
code = 'MYPY'
291+
include_patterns = [
292+
# TODO(https://github.com/pytorch/executorch/issues/7441): Gradually start enabling all folders.
293+
# 'backends/**/*.py',
294+
'build/**/*.py',
295+
# 'codegen/**/*.py',
296+
# 'devtools/**/*.py',
297+
# 'docs/**/*.py',
298+
# 'examples/**/*.py',
299+
# 'exir/**/*.py',
300+
# 'extension/**/*.py',
301+
'kernels/**/*.py',
302+
# 'profiler/**/*.py',
303+
'runtime/**/*.py',
304+
'scripts/**/*.py',
305+
# 'test/**/*.py',
306+
# 'util/**/*.py',
307+
'*.py',
308+
]
309+
exclude_patterns = [
310+
'third-party/**',
311+
'**/third-party/**',
312+
'scripts/check_binary_dependencies.py',
313+
]
314+
command = [
315+
'python',
316+
'-m',
317+
'lintrunner_adapters',
318+
'run',
319+
'mypy_linter',
320+
'--config=.mypy.ini',
321+
'--show-disable',
322+
'--',
323+
'@{{PATHSFILE}}'
324+
]
325+
init_command = [
326+
'python',
327+
'-m',
328+
'lintrunner_adapters',
329+
'run',
330+
'pip_init',
331+
'--dry-run={{DRYRUN}}',
332+
'--requirement=requirements-lintrunner.txt',
333+
]

.mypy.ini

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
[mypy]
2+
allow_redefinition = True
3+
warn_unused_configs = True
4+
warn_redundant_casts = True
5+
show_error_codes = True
6+
show_column_numbers = True
7+
disallow_untyped_decorators = True
8+
follow_imports = normal
9+
local_partial_types = True
10+
enable_error_code = possibly-undefined
11+
warn_unused_ignores = False
12+
13+
# TODO(https://github.com/pytorch/executorch/issues/7441): Remove this
14+
# disable_error_code = import-untyped
15+
16+
files =
17+
backends,
18+
codegen,
19+
devtools
20+
examples,
21+
exir,
22+
extension,
23+
kernels,
24+
profiler,
25+
runtime,
26+
scripts,
27+
util
28+
29+
mypy_path = executorch
30+
31+
[mypy-executorch.codegen.*]
32+
follow_untyped_imports = True
33+
34+
[mypy-executorch.extension.*]
35+
follow_untyped_imports = True
36+
37+
[mypy-executorch.exir.*]
38+
follow_untyped_imports = True
39+
40+
[mypy-executorch.kernels.*]
41+
follow_untyped_imports = True
42+
43+
[mypy-executorch.runtime.*]
44+
follow_untyped_imports = True
45+
46+
[mypy-torchgen.*]
47+
follow_untyped_imports = True
48+
49+
[mypy-setuptools.*]
50+
ignore_missing_imports = True
51+
52+
[mypy-buck_util]
53+
ignore_missing_imports = True
54+
55+
[mypy-tomllib]
56+
ignore_missing_imports = True
57+
58+
[mypy-zstd]
59+
ignore_missing_imports = True
60+
61+
[mypy-yaml]
62+
ignore_missing_imports = True
63+
64+
[mypy-ruamel]
65+
ignore_missing_imports = True

build/buck_util.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ def run(self, args: Sequence[str]) -> list[str]:
2424
"""Runs buck2 with the given args and returns its stdout as a sequence of lines."""
2525
try:
2626
cp: subprocess.CompletedProcess = subprocess.run(
27-
[self._path] + args, capture_output=True, cwd=BUCK_CWD, check=True
27+
[self._path] + args, # type: ignore[operator]
28+
capture_output=True,
29+
cwd=BUCK_CWD,
30+
check=True,
2831
)
2932
return [line.strip().decode("utf-8") for line in cp.stdout.splitlines()]
3033
except subprocess.CalledProcessError as ex:

build/extract_sources.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def __init__(
8585
base_dict: Optional[dict] = None,
8686
) -> None:
8787
self._state: Target._InitState = Target._InitState.UNINITIALIZED
88-
self._sources = frozenset()
88+
self._sources: frozenset[str] = frozenset()
8989

9090
self.name = name
9191
# Extend the base lists with the target-specific entries.

codegen/tools/__init__.py

Whitespace-only changes.

codegen/tools/gen_all_oplist.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def main(argv: List[Any]) -> None:
145145

146146
# Optionally check if the operators in the model file list are overlapping.
147147
if options.check_ops_not_overlapping:
148-
ops = {}
148+
ops = {} # type: ignore[var-annotated]
149149
for model_dict in model_dicts:
150150
for op_name in model_dict["operators"]:
151151
if op_name in ops:

codegen/tools/gen_oplist.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class KernelType(IntEnum):
7979

8080

8181
def _get_operators(model_file: str) -> List[str]:
82-
from executorch.codegen.tools.selective_build import (
82+
from executorch.codegen.tools.selective_build import ( # type: ignore[import-not-found]
8383
_get_program_from_buffer,
8484
_get_program_operators,
8585
)
@@ -96,7 +96,7 @@ def _get_operators(model_file: str) -> List[str]:
9696

9797
def _get_kernel_metadata_for_model(model_file: str) -> Dict[str, List[str]]:
9898

99-
from executorch.codegen.tools.selective_build import (
99+
from executorch.codegen.tools.selective_build import ( # type: ignore[import-not-found]
100100
_get_io_metadata_for_program_operators,
101101
_get_program_from_buffer,
102102
_IOMetaData,
@@ -159,7 +159,7 @@ def _dump_yaml(
159159
include_all_operators: bool = False,
160160
):
161161
# no debug info yet
162-
output = {}
162+
output: dict[str, Any] = {}
163163
operators: Dict[str, Dict[str, object]] = {}
164164
for op_name in op_list:
165165
op = SelectiveBuildOperator.from_yaml_dict(
@@ -208,7 +208,7 @@ def gen_oplist(
208208
assert output_path, "Need to provide output_path for dumped yaml file."
209209
op_set = set()
210210
source_name = None
211-
et_kernel_metadata = {}
211+
et_kernel_metadata = {} # type: ignore[var-annotated]
212212
if root_ops:
213213
# decide delimiter
214214
delimiter = "," if "," in root_ops else " "

codegen/tools/gen_ops_def.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def get_operators(model_file: str) -> List[Operator]:
3131

3232
def dump_yaml(model_file: str, output_file: str) -> None:
3333
ops = get_operators(model_file)
34-
m = []
34+
m = [] # type: ignore[var-annotated]
3535
for op in ops:
3636
if op.name.startswith("aten::"):
3737
schemas = torch._C._jit_get_schemas_for_operator(op.name)

codegen/tools/merge_yaml.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ def get_canonical_opname(func: object) -> str:
4444
Returns:
4545
str: canonical name of the operator
4646
"""
47-
# pyre-ignore
48-
opname = func["op"] if "op" in func else func["func"].split("(")[0]
47+
opname = func["op"] if "op" in func else func["func"].split("(")[0] # type: ignore
4948
if "::" not in opname:
5049
opname = "aten::" + opname
5150
return opname

codegen/tools/test/test_gen_oplist_real_model.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
_get_operators,
1414
)
1515

16-
from libfb.py import parutil
16+
from libfb.py import parutil # type: ignore[import-not-found]
1717

1818
MODEL_PATH: Final[str] = parutil.get_file_path("ModuleLinear.pte", pkg=__package__)
1919

kernels/test/gen_supported_features_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import unittest
99

1010
import yaml
11-
from executorch.kernels.test.gen_supported_features import (
11+
from executorch.kernels.test.gen_supported_features import ( # type: ignore[import-not-found]
1212
generate_definition,
1313
generate_header,
1414
)

requirements-lintrunner.txt

+3
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ usort==1.0.8.post1
2020
# Other linters
2121
clang-format==18.1.3
2222
cmakelint==1.4.1
23+
24+
# MyPy
25+
mypy==1.14.1

scripts/file_size_compare.py

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def main() -> int:
170170
)
171171
elif args.max_size is not None:
172172
return compare_against_max(args.compare_file, args.max_size)
173+
return 0
173174

174175

175176
if __name__ == "__main__":

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def string(cls) -> str:
145145
open(os.path.join(cls._root_dir(), "version.txt")).read().strip()
146146
)
147147
if cls.git_hash():
148-
version += "+" + cls.git_hash()[:7]
148+
version += "+" + cls.git_hash()[:7] # type: ignore[index]
149149
cls.__string_attr = version
150150
return cls.__string_attr
151151

0 commit comments

Comments
 (0)