diff --git a/check_source/check_mypy.py b/check_source/check_mypy.py new file mode 100755 index 0000000000000000000000000000000000000000..92a6bea0da1894b9ad6ea14973e4dbea0258b07d --- /dev/null +++ b/check_source/check_mypy.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +import os +from os.path import join, splitext + +from check_mypy_config import PATHS, BLACKLIST + +from typing import ( + Any, + Callable, + Generator, + Optional, + Tuple, + Dict, +) + +FileAndArgs = Tuple[str, Tuple[Any, ...], Dict[str, str]] + +# print(PATHS) +SOURCE_EXT = ( + # Python + ".py", +) + + +def is_source(filename: str) -> bool: + return filename.endswith(SOURCE_EXT) + + +def path_iter( + path: str, + filename_check: Optional[Callable[[str], bool]] = None, +) -> Generator[str, None, None]: + for dirpath, dirnames, filenames in os.walk(path): + # skip ".git" + dirnames[:] = [d for d in dirnames if not d.startswith(".")] + + for filename in filenames: + if filename.startswith("."): + continue + filepath = join(dirpath, filename) + if filename_check is None or filename_check(filepath): + yield filepath + + +def path_expand_with_args( + paths_and_args: Tuple[FileAndArgs, ...], + filename_check: Optional[Callable[[str], bool]] = None, +) -> Generator[FileAndArgs, None, None]: + for f_and_args in paths_and_args: + f, f_args = f_and_args[0], f_and_args[1:] + if not os.path.exists(f): + print("Missing:", f) + elif os.path.isdir(f): + for f_iter in path_iter(f, filename_check): + yield (f_iter, *f_args) + else: + yield (f, *f_args) + + +def main() -> None: + import sys + import subprocess + import shlex + + # Fixed location, so change the current working directory doesn't create cache everywhere. + cache_dir = os.path.join(os.getcwd(), ".mypy_cache") + + if os.path.samefile(sys.argv[-1], __file__): + paths = path_expand_with_args(PATHS, is_source) + else: + paths = path_expand_with_args( + tuple((p, (), {}) for p in sys.argv[1:]), + is_source, + ) + + for f, extra_args, extra_env in paths: + if f in BLACKLIST: + continue + + if not extra_args: + extra_args = () + if not extra_env: + extra_env = {} + + # print(f) + print(repr(f[len('/src/blender/'):]) + ',') + cmd = ( + "mypy", + "--strict", + "--cache-dir=" + cache_dir, + "--color-output", + f, + *extra_args, + ) + # p = subprocess.Popen(cmd, env=extra_env, stdout=sys.stdout, stderr=sys.stderr) + + if extra_env: + for k, v in extra_env.items(): + os.environ[k] = v + + os.chdir(os.path.dirname(f)) + + os.system(" ".join([shlex.quote(arg) for arg in cmd])) + + if extra_env: + for k in extra_env.keys(): + del os.environ[k] + + +if __name__ == "__main__": + main() diff --git a/check_source/check_mypy_config.py b/check_source/check_mypy_config.py new file mode 100644 index 0000000000000000000000000000000000000000..85a6063a8ba65dda6c3a6e5c31f80d91797bac7f --- /dev/null +++ b/check_source/check_mypy_config.py @@ -0,0 +1,45 @@ + +import os +from typing import ( + Any, + Tuple, + Dict, +) + +PATHS: Tuple[Tuple[str, Tuple[Any, ...], Dict[str, str]], ...] = ( + ("build_files/cmake/", (), {'MYPYPATH': "modules"}), + ("doc/manpage/blender.1.py", (), {}), + ("source/tools/check_source/", (), {'MYPYPATH': "modules"}), + ("source/tools/utils_maintenance/", (), {'MYPYPATH': "modules"}), +) + +SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..", "..")))) + +BLACKLIST = set( + os.path.join(SOURCE_DIR, p.replace("/", os.sep)) + for p in + ( + "build_files/cmake/clang_array_check.py", + "build_files/cmake/cmake_netbeans_project.py", + "build_files/cmake/cmake_print_build_options.py", + "build_files/cmake/cmake_qtcreator_project.py", + "build_files/cmake/cmake_static_check_smatch.py", + "build_files/cmake/cmake_static_check_sparse.py", + "build_files/cmake/cmake_static_check_splint.py", + "source/tools/check_source/check_descriptions.py", + "source/tools/check_source/check_header_duplicate.py", + "source/tools/check_source/check_spelling.py", + "source/tools/check_source/check_unused_defines.py", + "source/tools/utils_maintenance/blender_menu_search_coverage.py", + "source/tools/utils_maintenance/blender_update_themes.py", + "source/tools/utils_maintenance/clang_format_paths.py", + "source/tools/utils_maintenance/trailing_space_clean.py", + "source/tools/utils_maintenance/trailing_space_clean_config.py", + ) +) + +PATHS = tuple( + (os.path.join(SOURCE_DIR, p_items[0].replace("/", os.sep)), *p_items[1:]) + for p_items in PATHS +)