mirror of
https://github.com/davidhalter/django-stubs.git
synced 2026-02-27 12:02:20 +08:00
move to custom pytest plugin test runner, fix tests, add Any fallback to ForeignKey
This commit is contained in:
@@ -132,13 +132,11 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str]) -> None:
|
||||
num_skip_end = _num_skipped_suffix_lines(expected, actual)
|
||||
|
||||
error_message += 'Expected:\n'
|
||||
# sys.stderr.write('Expected:\n')
|
||||
|
||||
# If omit some lines at the beginning, indicate it by displaying a line
|
||||
# with '...'.
|
||||
if num_skip_start > 0:
|
||||
error_message += ' ...\n'
|
||||
# sys.stderr.write(' ...\n')
|
||||
|
||||
# Keep track of the first different line.
|
||||
first_diff = -1
|
||||
@@ -151,51 +149,37 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str]) -> None:
|
||||
if first_diff < 0:
|
||||
first_diff = i
|
||||
error_message += ' {:<45} (diff)'.format(expected[i])
|
||||
# sys.stderr.write(' {:<45} (diff)'.format(expected[i]))
|
||||
else:
|
||||
e = expected[i]
|
||||
error_message += ' ' + e[:width]
|
||||
# sys.stderr.write(' ' + e[:width])
|
||||
if len(e) > width:
|
||||
error_message += '...'
|
||||
# sys.stderr.write('...')
|
||||
error_message += '\n'
|
||||
# sys.stderr.write('\n')
|
||||
if num_skip_end > 0:
|
||||
error_message += ' ...\n'
|
||||
# sys.stderr.write(' ...\n')
|
||||
|
||||
error_message += 'Actual:\n'
|
||||
# sys.stderr.write('Actual:\n')
|
||||
|
||||
if num_skip_start > 0:
|
||||
error_message += ' ...\n'
|
||||
# sys.stderr.write(' ...\n')
|
||||
|
||||
for j in range(num_skip_start, len(actual) - num_skip_end):
|
||||
if j >= len(expected) or expected[j] != actual[j]:
|
||||
error_message += ' {:<45} (diff)'.format(actual[j])
|
||||
# sys.stderr.write(' {:<45} (diff)'.format(actual[j]))
|
||||
else:
|
||||
a = actual[j]
|
||||
error_message += ' ' + a[:width]
|
||||
# sys.stderr.write(' ' + a[:width])
|
||||
if len(a) > width:
|
||||
error_message += '...'
|
||||
# sys.stderr.write('...')
|
||||
error_message += '\n'
|
||||
# sys.stderr.write('\n')
|
||||
if actual == []:
|
||||
error_message += ' (empty)\n'
|
||||
# sys.stderr.write(' (empty)\n')
|
||||
if num_skip_end > 0:
|
||||
error_message += ' ...\n'
|
||||
# sys.stderr.write(' ...\n')
|
||||
|
||||
error_message += '\n'
|
||||
# sys.stderr.write('\n')
|
||||
|
||||
if first_diff >= 0 and first_diff < len(actual) and (
|
||||
if 0 <= first_diff < len(actual) and (
|
||||
len(expected[first_diff]) >= MIN_LINE_LENGTH_FOR_ALIGNMENT
|
||||
or len(actual[first_diff]) >= MIN_LINE_LENGTH_FOR_ALIGNMENT):
|
||||
# Display message that helps visualize the differences between two
|
||||
|
||||
@@ -189,9 +189,9 @@ class MypyTypecheckItem(pytest.Item):
|
||||
main_fpath.write_text(self.source_code)
|
||||
mypy_cmd_options.append(str(main_fpath))
|
||||
|
||||
stdout, _, _ = mypy_api.run(mypy_cmd_options)
|
||||
stdout, stderr, returncode = mypy_api.run(mypy_cmd_options)
|
||||
output_lines = []
|
||||
for line in stdout.splitlines():
|
||||
for line in (stdout + stderr).splitlines():
|
||||
if ':' not in line:
|
||||
continue
|
||||
out_fpath, res_line = line.split(':', 1)
|
||||
@@ -199,15 +199,18 @@ class MypyTypecheckItem(pytest.Item):
|
||||
output_lines.append(line.strip().replace('.py', ''))
|
||||
|
||||
for module in test_specific_modules:
|
||||
if module in sys.modules:
|
||||
del sys.modules[module]
|
||||
raise ValueError
|
||||
parts = module.split('.')
|
||||
for i in range(len(parts)):
|
||||
parent_module = '.'.join(parts[:i + 1])
|
||||
if parent_module in sys.modules:
|
||||
del sys.modules[parent_module]
|
||||
|
||||
assert_string_arrays_equal(expected=self.expected_output_lines,
|
||||
actual=output_lines)
|
||||
|
||||
def prepare_mypy_cmd_options(self, config_file_path: Path) -> List[str]:
|
||||
mypy_cmd_options = [
|
||||
'--show-traceback',
|
||||
'--raise-exceptions',
|
||||
'--no-silence-site-packages'
|
||||
]
|
||||
python_version = '.'.join([str(part) for part in sys.version_info[:2]])
|
||||
@@ -238,7 +241,7 @@ class MypyTypecheckItem(pytest.Item):
|
||||
exception_repr.reprtraceback.reprentries = [repr_tb_entry]
|
||||
return exception_repr
|
||||
else:
|
||||
return super().repr_failure(excinfo, style='short')
|
||||
return super().repr_failure(excinfo, style='native')
|
||||
|
||||
def reportinfo(self):
|
||||
return self.fspath, None, get_class_qualname(self.klass) + '::' + self.name
|
||||
@@ -266,7 +269,7 @@ class MypyTestsCollector(pytest.Class):
|
||||
current_testcase = cast(MypyTypecheckTestCase, self.obj())
|
||||
ini_file_contents = self.get_ini_file_contents(current_testcase.ini_file())
|
||||
for attr_name in dir(current_testcase):
|
||||
if attr_name.startswith('_test_'):
|
||||
if attr_name.startswith('test_'):
|
||||
attr = getattr(self.obj, attr_name)
|
||||
if inspect.isfunction(attr):
|
||||
first_line_lnum, source_lines = get_func_first_lnum(attr)
|
||||
|
||||
@@ -9,7 +9,7 @@ class TestParseSettingsFromFile(BaseDjangoPluginTestCase):
|
||||
|
||||
reveal_type(settings.ROOT_DIR) # E: Revealed type is 'builtins.str'
|
||||
reveal_type(settings.OBJ) # E: Revealed type is 'django.utils.functional.LazyObject'
|
||||
reveal_type(settings.NUMBERS) # E: Revealed type is 'builtins.list[Any]'
|
||||
reveal_type(settings.NUMBERS) # E: Revealed type is 'builtins.list[builtins.str]'
|
||||
reveal_type(settings.DICT) # E: Revealed type is 'builtins.dict[Any, Any]'
|
||||
|
||||
@file('mysettings.py')
|
||||
@@ -34,4 +34,4 @@ class TestSettingInitializableToNone(BaseDjangoPluginTestCase):
|
||||
@file('mysettings.py')
|
||||
def mysettings_py_file(self):
|
||||
SECRET_KEY = 112233
|
||||
NONE_SETTING = None
|
||||
NONE_SETTING: object = None
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
from test.pytest_plugin import file, reveal_type, env
|
||||
from test.pytest_tests.base import BaseDjangoPluginTestCase
|
||||
|
||||
|
||||
class TestForeignKey(BaseDjangoPluginTestCase):
|
||||
@env(DJANGO_SETTINGS_MODULE='mysettings')
|
||||
def _test_to_parameter_could_be_specified_as_string(self):
|
||||
from apps.myapp.models import Publisher
|
||||
|
||||
publisher = Publisher()
|
||||
reveal_type(publisher.books) # E: Revealed type is 'django.db.models.query.QuerySet[apps.myapp2.models.Book]'
|
||||
|
||||
# @env(DJANGO_SETTINGS_MODULE='mysettings')
|
||||
# def _test_creates_underscore_id_attr(self):
|
||||
# from apps.myapp2.models import Book
|
||||
#
|
||||
# book = Book()
|
||||
# reveal_type(book.publisher) # E: Revealed type is 'apps.myapp.models.Publisher'
|
||||
# reveal_type(book.publisher_id) # E: Revealed type is 'builtins.int'
|
||||
|
||||
@file('mysettings.py')
|
||||
def mysettings(self):
|
||||
SECRET_KEY = '112233'
|
||||
ROOT_DIR = '<TMP>'
|
||||
APPS_DIR = '<TMP>/apps'
|
||||
|
||||
INSTALLED_APPS = ('apps.myapp', 'apps.myapp2')
|
||||
|
||||
@file('apps/myapp/models.py', make_parent_packages=True)
|
||||
def apps_myapp_models(self):
|
||||
from django.db import models
|
||||
|
||||
class Publisher(models.Model):
|
||||
pass
|
||||
|
||||
@file('apps/myapp2/models.py', make_parent_packages=True)
|
||||
def apps_myapp2_models(self):
|
||||
from django.db import models
|
||||
|
||||
class Book(models.Model):
|
||||
publisher = models.ForeignKey(to='myapp.Publisher', on_delete=models.CASCADE,
|
||||
related_name='books')
|
||||
|
||||
|
||||
class TestOneToOneField(BaseDjangoPluginTestCase):
|
||||
@env(DJANGO_SETTINGS_MODULE='mysettings')
|
||||
def test_to_parameter_could_be_specified_as_string(self):
|
||||
from apps.myapp.models import User
|
||||
|
||||
user = User()
|
||||
reveal_type(user.profile) # E: Revealed type is 'apps.myapp2.models.Profile'
|
||||
|
||||
@file('mysettings.py')
|
||||
def mysettings(self):
|
||||
SECRET_KEY = '112233'
|
||||
ROOT_DIR = '<TMP>'
|
||||
APPS_DIR = '<TMP>/apps'
|
||||
|
||||
INSTALLED_APPS = ('apps.myapp', 'apps.myapp2')
|
||||
|
||||
@file('apps/myapp/models.py', make_parent_packages=True)
|
||||
def apps_myapp_models(self):
|
||||
from django.db import models
|
||||
|
||||
class User(models.Model):
|
||||
pass
|
||||
|
||||
@file('apps/myapp2/models.py', make_parent_packages=True)
|
||||
def apps_myapp2_models(self):
|
||||
from django.db import models
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(to='myapp.User', on_delete=models.CASCADE,
|
||||
related_name='profile')
|
||||
Reference in New Issue
Block a user