mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-07 20:54:29 +08:00
move to plain python script for typechecking
This commit is contained in:
@@ -10,7 +10,7 @@ jobs:
|
|||||||
- name: "Typecheck Django test suite"
|
- name: "Typecheck Django test suite"
|
||||||
python: 3.7
|
python: 3.7
|
||||||
script: |
|
script: |
|
||||||
xonsh ./scripts/typecheck_django_tests.xsh
|
python ./scripts/typecheck_tests.py
|
||||||
|
|
||||||
- name: "Run plugin test suite with python 3.7"
|
- name: "Run plugin test suite with python 3.7"
|
||||||
python: 3.7
|
python: 3.7
|
||||||
@@ -25,7 +25,8 @@ jobs:
|
|||||||
|
|
||||||
before_install: |
|
before_install: |
|
||||||
# Upgrade pip, setuptools, and wheel
|
# Upgrade pip, setuptools, and wheel
|
||||||
pip install -U pip setuptools wheel xonsh
|
pip install -U pip setuptools wheel
|
||||||
|
|
||||||
install: |
|
install: |
|
||||||
pip install -r ./dev-requirements.txt
|
pip install -r ./dev-requirements.txt
|
||||||
|
pip install -r ./scripts/typecheck-tests-requirements.txt
|
||||||
|
|||||||
1
scripts/typecheck-tests-requirements.txt
Normal file
1
scripts/typecheck-tests-requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
gitpython
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if not os.path.exists('./django-sources'):
|
|
||||||
git clone -b stable/2.1.x https://github.com/django/django.git django-sources
|
|
||||||
|
|
||||||
IGNORED_ERROR_PATTERNS = [
|
|
||||||
'Need type annotation for',
|
|
||||||
'already defined on',
|
|
||||||
'Cannot assign to a',
|
|
||||||
'cannot perform relative import',
|
|
||||||
'broken_app',
|
|
||||||
'LazySettings',
|
|
||||||
'Cannot infer type of lambda',
|
|
||||||
'Incompatible types in assignment (expression has type "Callable[',
|
|
||||||
'"Callable[[Any], Any]" has no attribute',
|
|
||||||
'Invalid value for a to= parameter'
|
|
||||||
]
|
|
||||||
TESTS_DIRS = [
|
|
||||||
'absolute_url_overrides',
|
|
||||||
'admin_*',
|
|
||||||
'aggregation',
|
|
||||||
'aggregation_regress',
|
|
||||||
'annotations',
|
|
||||||
'app_loading',
|
|
||||||
]
|
|
||||||
|
|
||||||
def check_file_in_the_current_directory(directory, fname):
|
|
||||||
rc = 0
|
|
||||||
cd @(directory)
|
|
||||||
with ${...}.swap(FNAME=fname):
|
|
||||||
for line in $(mypy --config-file ../../../scripts/mypy.ini $FNAME).split('\n'):
|
|
||||||
for pattern in IGNORED_ERROR_PATTERNS:
|
|
||||||
if pattern in line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if line:
|
|
||||||
rc = 1
|
|
||||||
print(line)
|
|
||||||
cd -
|
|
||||||
return rc
|
|
||||||
|
|
||||||
def parse_ls_output_into_fnames(output):
|
|
||||||
fnames = []
|
|
||||||
for line in output.splitlines()[1:]:
|
|
||||||
fnames.append(line.split()[-1])
|
|
||||||
return fnames
|
|
||||||
|
|
||||||
all_tests_dirs = []
|
|
||||||
for test_dir in TESTS_DIRS:
|
|
||||||
with ${...}.swap(TEST_DIR=test_dir):
|
|
||||||
dirs = g`django-sources/tests/$TEST_DIR`
|
|
||||||
all_tests_dirs.extend(dirs)
|
|
||||||
|
|
||||||
rc = 0
|
|
||||||
for tests_dir in all_tests_dirs:
|
|
||||||
print('Checking ' + tests_dir)
|
|
||||||
abs_dir = os.path.join(os.getcwd(), tests_dir)
|
|
||||||
|
|
||||||
with ${...}.swap(ABS_DIR=abs_dir):
|
|
||||||
ls_output = $(ls -lhv --color=auto -F --group-directories-first $ABS_DIR)
|
|
||||||
for fname in parse_ls_output_into_fnames(ls_output):
|
|
||||||
path_to_check = os.path.join(abs_dir, fname)
|
|
||||||
current_step_rc = check_file_in_the_current_directory(abs_dir, fname)
|
|
||||||
if current_step_rc != 0:
|
|
||||||
rc = current_step_rc
|
|
||||||
|
|
||||||
sys.exit(rc)
|
|
||||||
96
scripts/typecheck_tests.py
Normal file
96
scripts/typecheck_tests.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from git import Repo
|
||||||
|
from mypy import build
|
||||||
|
from mypy.main import process_options
|
||||||
|
|
||||||
|
DJANGO_BRANCH = 'stable/2.1.x'
|
||||||
|
DJANGO_COMMIT_SHA = '03219b5f709dcd5b0bfacd963508625557ec1ef0'
|
||||||
|
IGNORED_ERROR_PATTERNS = [
|
||||||
|
'Need type annotation for',
|
||||||
|
'already defined on',
|
||||||
|
'Cannot assign to a',
|
||||||
|
'cannot perform relative import',
|
||||||
|
'broken_app',
|
||||||
|
'LazySettings',
|
||||||
|
'Cannot infer type of lambda',
|
||||||
|
'Incompatible types in assignment (expression has type "Callable[',
|
||||||
|
'"Callable[[Any], Any]" has no attribute',
|
||||||
|
'"Callable[[Any, Any], Any]" has no attribute',
|
||||||
|
'Invalid value for a to= parameter',
|
||||||
|
'"HttpResponseBase" has no attribute "user"'
|
||||||
|
]
|
||||||
|
TESTS_DIRS = [
|
||||||
|
'absolute_url_overrides',
|
||||||
|
# 'admin_*'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def cd(path):
|
||||||
|
"""Context manager to temporarily change working directories"""
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
prev_cwd = Path.cwd().as_posix()
|
||||||
|
if isinstance(path, Path):
|
||||||
|
path = path.as_posix()
|
||||||
|
os.chdir(str(path))
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.chdir(prev_cwd)
|
||||||
|
|
||||||
|
|
||||||
|
def is_ignored(line: str) -> bool:
|
||||||
|
for pattern in IGNORED_ERROR_PATTERNS:
|
||||||
|
if pattern in line:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_with_mypy(abs_path: Path, config_file_path: Path) -> int:
|
||||||
|
error_happened = False
|
||||||
|
with cd(abs_path):
|
||||||
|
sources, options = process_options(['--config-file', str(config_file_path), str(abs_path)])
|
||||||
|
res = build.build(sources, options)
|
||||||
|
for error_line in res.errors:
|
||||||
|
if not is_ignored(error_line):
|
||||||
|
error_happened = True
|
||||||
|
print(error_line)
|
||||||
|
return int(error_happened)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
project_directory = Path(__file__).parent.parent
|
||||||
|
mypy_config_file = (project_directory / 'scripts' / 'mypy.ini').absolute()
|
||||||
|
repo_directory = project_directory / 'django-sources'
|
||||||
|
tests_root = repo_directory / 'tests'
|
||||||
|
global_rc = 0
|
||||||
|
|
||||||
|
# clone Django repository, if it does not exist
|
||||||
|
if not repo_directory.exists():
|
||||||
|
repo = Repo.clone_from('https://github.com/django/django.git', repo_directory)
|
||||||
|
else:
|
||||||
|
repo = Repo(repo_directory)
|
||||||
|
repo.remote().pull(DJANGO_COMMIT_SHA)
|
||||||
|
|
||||||
|
branch = repo.heads[DJANGO_BRANCH]
|
||||||
|
branch.checkout()
|
||||||
|
assert repo.active_branch.name == DJANGO_BRANCH
|
||||||
|
assert repo.active_branch.commit.hexsha == DJANGO_COMMIT_SHA
|
||||||
|
|
||||||
|
for dirname in TESTS_DIRS:
|
||||||
|
paths = glob.glob(str(tests_root / dirname))
|
||||||
|
for path in paths:
|
||||||
|
abs_path = (project_directory / path).absolute()
|
||||||
|
|
||||||
|
print(f'Checking {abs_path.as_uri()}')
|
||||||
|
rc = check_with_mypy(abs_path, mypy_config_file)
|
||||||
|
if rc != 0:
|
||||||
|
global_rc = 1
|
||||||
|
|
||||||
|
sys.exit(rc)
|
||||||
Reference in New Issue
Block a user