move to custom pytest plugin test runner, fix tests, add Any fallback to ForeignKey

This commit is contained in:
Maxim Kurnikov
2018-11-28 00:37:04 +03:00
parent f59cfe6371
commit 64bc053056
14 changed files with 451 additions and 366 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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')