Files
django-stubs/scripts/typecheck_tests.py
Nikita Sobolev 0bb1182c42 Fix CI (#1108)
* Fix CI

* Fix CI

* Fix CI

* Fix CI

* APply black

* APply black

* Fix mypy

* Fix mypy errors in django-stubs

* Fix format

* Fix plugin

* Do not patch builtins by default

* Fix mypy

* Only run mypy on 3.10 for now

* Only run mypy on 3.10 for now

* WHAT THE HELL

* Enable strict mode in mypy

* Enable strict mode in mypy

* Fix tests

* Fix tests

* Debug

* Debug

* Fix tests

* Fix tests

* Add TYPE_CHECKING debug

* Caching maybe?

* Caching maybe?

* Try explicit `${{ matrix.python-version }}`

* Remove debug

* Fix typing

* Finally
2022-08-26 13:22:55 +03:00

122 lines
4.1 KiB
Python

import itertools
import shutil
import subprocess
import sys
from argparse import ArgumentParser
from collections import defaultdict
from distutils import spawn
from typing import DefaultDict, List, Pattern, Union
from scripts.enabled_test_modules import EXTERNAL_MODULES, IGNORED_ERRORS, IGNORED_MODULES, MOCK_OBJECTS
from scripts.git_helpers import checkout_django_branch
from scripts.paths import DJANGO_SOURCE_DIRECTORY, PROJECT_DIRECTORY
DJANGO_COMMIT_REFS = {
"2.2": "44e7cca62382f2535ed0f5d2842b433f0bd23a57",
"3.2": "0153a63a674937e4a56d9d5e4ca2d629b011fbde",
"4.0": "67d0c4644acfd7707be4a31e8976f865509b09ac",
}
DEFAULT_DJANGO_VERSION = "3.2"
_DictToSearch = DefaultDict[str, DefaultDict[Union[str, Pattern[str]], int]]
def get_unused_ignores(ignored_message_freq: _DictToSearch) -> List[str]:
unused_ignores = []
for root_key, patterns in IGNORED_ERRORS.items():
for pattern in patterns:
if ignored_message_freq[root_key][pattern] == 0 and pattern not in itertools.chain(
EXTERNAL_MODULES, MOCK_OBJECTS
):
unused_ignores.append(f"{root_key}: {pattern}")
return unused_ignores
def does_pattern_fit(pattern: Union[Pattern[str], str], line: str) -> bool:
if isinstance(pattern, Pattern):
if pattern.search(line):
return True
else:
if pattern in line:
return True
return False
def is_ignored(line: str, test_folder_name: str, *, ignored_message_freqs: _DictToSearch) -> bool:
if "runtests" in line:
return True
if test_folder_name in IGNORED_MODULES:
return True
for pattern in IGNORED_ERRORS.get(test_folder_name, []):
if does_pattern_fit(pattern, line):
ignored_message_freqs[test_folder_name][pattern] += 1
return True
for pattern in IGNORED_ERRORS["__common__"]:
if does_pattern_fit(pattern, line):
ignored_message_freqs["__common__"][pattern] += 1
return True
return False
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("--django_version", default=DEFAULT_DJANGO_VERSION)
django_version = parser.parse_args().django_version
subprocess.check_call([sys.executable, "-m", "pip", "install", f"Django=={django_version}.*"])
commit_sha = DJANGO_COMMIT_REFS[django_version]
repo = checkout_django_branch(django_version, commit_sha)
mypy_config_file = (PROJECT_DIRECTORY / "mypy.ini").absolute()
mypy_cache_dir = PROJECT_DIRECTORY / ".mypy_cache"
tests_root = DJANGO_SOURCE_DIRECTORY / "tests"
global_rc = 0
try:
mypy_options = [
"--cache-dir",
str(mypy_cache_dir),
"--config-file",
str(mypy_config_file),
"--show-traceback",
"--no-error-summary",
"--hide-error-context",
]
mypy_options += [str(tests_root)]
mypy_executable = spawn.find_executable("mypy")
mypy_argv = [mypy_executable, *mypy_options]
completed = subprocess.run(
mypy_argv, # type: ignore
env={"PYTHONPATH": str(tests_root), "TYPECHECK_TESTS": "1"},
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
output = completed.stdout.decode()
ignored_message_freqs: _DictToSearch = defaultdict(lambda: defaultdict(int))
sorted_lines = sorted(output.splitlines())
for line in sorted_lines:
try:
path_to_error = line.split(":")[0]
test_folder_name = path_to_error.split("/")[2]
except IndexError:
test_folder_name = "unknown"
if not is_ignored(line, test_folder_name, ignored_message_freqs=ignored_message_freqs):
global_rc = 1
print(line)
unused_ignores = get_unused_ignores(ignored_message_freqs)
if unused_ignores:
print("UNUSED IGNORES ------------------------------------------------")
print("\n".join(unused_ignores))
sys.exit(global_rc)
except BaseException as exc:
shutil.rmtree(mypy_cache_dir, ignore_errors=True)
raise exc