Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from mypy.stats import dump_type_stats
from mypy.types import Type
from mypy.version import __version__
from mypy.defaults import allow_fixtures


# We need to know the location of this file to load data, but
Expand Down Expand Up @@ -143,8 +144,9 @@ def build(sources: List[BuildSource],
# debug). This is a test-only feature, so assume our files are laid out
# as in the source tree.
root_dir = dirname(dirname(__file__))
lib_path.insert(0, os.path.join(root_dir, 'test-data', 'unit', 'lib-stub'))
else:
if allow_fixtures():
lib_path.insert(0, os.path.join(root_dir, 'test-data', 'unit', 'lib-stub'))
if not options.use_builtins_fixtures or not allow_fixtures():
for source in sources:
if source.path:
# Include directory of the program file in the module search path.
Expand Down
5 changes: 5 additions & 0 deletions mypy/defaults.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import os
PYTHON2_VERSION = (2, 7)
PYTHON3_VERSION = (3, 6)
CACHE_DIR = '.mypy_cache'
CONFIG_FILE = 'mypy.ini'


def allow_fixtures() -> bool:
return not os.environ.get('NO_BUILTINS_FIXTURES', False)
8 changes: 6 additions & 2 deletions mypy/test/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

from mypy.myunit import TestCase, SkipTestCaseException

from mypy.defaults import allow_fixtures


def parse_test_cases(
path: str,
Expand Down Expand Up @@ -63,7 +65,8 @@ def parse_test_cases(
assert arg is not None
file_entry = (join(base_path, arg), '\n'.join(p[i].data))
if p[i].id == 'file':
files.append(file_entry)
if arg != 'builtins.py' or allow_fixtures():
files.append(file_entry)
elif p[i].id == 'outfile':
output_files.append(file_entry)
elif p[i].id in ('builtins', 'builtins_py2'):
Expand All @@ -77,7 +80,8 @@ def parse_test_cases(
# Python 2
fnam = '__builtin__.pyi'
with open(mpath) as f:
files.append((join(base_path, fnam), f.read()))
if allow_fixtures():
files.append((join(base_path, fnam), f.read()))
elif p[i].id == 'stale':
arg = p[i].arg
if arg is None:
Expand Down
44 changes: 43 additions & 1 deletion mypy/test/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,51 @@
import re
import os

from typing import List, Dict, Tuple
from typing import List, Dict, Tuple, Iterable
from itertools import groupby

from mypy import defaults
from mypy.myunit import AssertionFailure
from mypy.test.data import DataDrivenTestCase
from mypy.defaults import allow_fixtures


# AssertStringArraysEqual displays special line alignment helper messages if
# the first different line has at least this many characters,
MIN_LINE_LENGTH_FOR_ALIGNMENT = 5
# Maximum lines of actual error output to print (to prevent hitting log size limit)
MAX_ACTUAL_LINES = float('inf') if allow_fixtures() else 25


# should match 'tmp/foo.pyi', 'main.py', etc.
MODULE_NAME_PATTERN = r'[\w/.]'
# we really want to be precise because accidentally matching the pattern
# may cause failed tests to look like they pass
# should match 'tmp/foo.pyi:15:30', 'tmp/foo.pyi:15', 'main.py:1:2', etc.
ERROR_PATTERN = re.compile(r'^{}+:(\d+):(\d+:)? error: '.format(MODULE_NAME_PATTERN))


class ErrorStr(str):
'''
Same as str, but compares equal iff it starts with the same file and line no prefix
'''
def __eq__(self, other: object) -> bool:
if not isinstance(other, str):
return False
m_self = re.search(ERROR_PATTERN, self)
m_other = re.search(ERROR_PATTERN, other)
if not m_self or not m_other: # we can't parse it, just use normal str comparison
return super().__eq__(other)
else: # parsed successfully, compare line no (ignore filename and column no)
return m_self.group(1) == m_other.group(1)


def skip_same_line_errors(iterable: Iterable) -> List:
'''
Converts all strings into ErrorStr objects, skips consecutive
duplicates, and returns the list (preserving original order)
'''
return [k for k, g in groupby(map(ErrorStr, iterable))]


def assert_string_arrays_equal(expected: List[str], actual: List[str],
Expand All @@ -23,6 +58,10 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str],

actual = clean_up(actual)

if not allow_fixtures():
actual = skip_same_line_errors(actual)
expected = skip_same_line_errors(expected)

if actual != expected:
num_skip_start = num_skipped_prefix_lines(expected, actual)
num_skip_end = num_skipped_suffix_lines(expected, actual)
Expand Down Expand Up @@ -60,6 +99,9 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str],
sys.stderr.write(' ...\n')

for j in range(num_skip_start, len(actual) - num_skip_end):
if j - num_skip_start > MAX_ACTUAL_LINES:
sys.stderr.write('... (additional lines suppressed)')
break
if j >= len(expected) or expected[j] != actual[j]:
sys.stderr.write(' {:<45} (diff)'.format(actual[j]))
else:
Expand Down
19 changes: 13 additions & 6 deletions mypy/test/testmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from mypy.test.testtypegen import ignore_node
from mypy.types import TypeStrVisitor, Type
from mypy.util import short_type
from mypy.defaults import allow_fixtures


files = [
Expand Down Expand Up @@ -96,6 +97,12 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
'Invalid output ({}, line {})'.format(testcase.file,
testcase.line))

def skip_module(self, mod: str) -> bool:
if allow_fixtures():
return mod == 'builtins'
else:
return mod not in ('__main__', 'target')

def build(self, source: str) -> Tuple[List[str], BuildManager, Dict[str, State]]:
options = Options()
options.use_builtins_fixtures = True
Expand Down Expand Up @@ -133,8 +140,8 @@ def dump(self,
def dump_asts(self, modules: Dict[str, MypyFile]) -> List[str]:
a = []
for m in sorted(modules):
if m == 'builtins':
# We don't support incremental checking of changes to builtins.
if self.skip_module(m):
# We don't support incremental checking of changes to builtins or stdlib.
continue
s = modules[m].accept(self.str_conv)
a.extend(s.splitlines())
Expand All @@ -143,8 +150,8 @@ def dump_asts(self, modules: Dict[str, MypyFile]) -> List[str]:
def dump_symbol_tables(self, modules: Dict[str, MypyFile]) -> List[str]:
a = []
for id in sorted(modules):
if id == 'builtins':
# We don't support incremental checking of changes to builtins.
if self.skip_module(id):
# We don't support incremental checking of changes to builtins or stdlib.
continue
a.extend(self.dump_symbol_table(id, modules[id].names))
return a
Expand All @@ -169,7 +176,7 @@ def format_symbol_table_node(self, node: SymbolTableNode) -> str:
def dump_typeinfos(self, modules: Dict[str, MypyFile]) -> List[str]:
a = []
for id in sorted(modules):
if id == 'builtins':
if self.skip_module(id):
continue
a.extend(self.dump_typeinfos_recursive(modules[id].names))
return a
Expand All @@ -192,7 +199,7 @@ def dump_types(self, graph: Dict[str, State]) -> List[str]:
# To make the results repeatable, we try to generate unique and
# deterministic sort keys.
for module_id in sorted(graph):
if module_id == 'builtins':
if self.skip_module(module_id):
continue
type_map = graph[module_id].type_checker.type_map
if type_map:
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def r(x) -> None: ...
r = l # E: Incompatible types in assignment (expression has type Callable[[Any, Any], None], variable has type Callable[[Any], None])

[case testSubtypingFunctionsImplicitNames]
from typing import Any

def f(a, b): pass
def g(c: Any, d: Any) -> Any: pass
Expand Down Expand Up @@ -1567,6 +1568,7 @@ class A(Generic[t]):


[case testRedefineFunction]
from typing import Any
def f(x) -> Any: pass
def g(x, y): pass
def h(x): pass
Expand Down
16 changes: 8 additions & 8 deletions test-data/unit/check-isinstance.test
Original file line number Diff line number Diff line change
Expand Up @@ -434,13 +434,13 @@ def foo() -> Union[int, str]: pass

x = foo()
x = 1
x = x + 1
x + 1
x = foo()
x = x + 1 # E: Unsupported operand types for + (likely involving Union)
x + 1 # E: Unsupported operand types for + (likely involving Union)
if isinstance(x, str):
x = x + 1 # E: Unsupported operand types for + ("str" and "int")
x + 1 # E: Unsupported operand types for + ("str" and "int")
x = 1
x = x + 1
x + 1

[builtins fixtures/isinstancelist.pyi]

Expand All @@ -463,11 +463,11 @@ from typing import Union
x = None # type: Union[int, str]

if isinstance(x, str):
x = x + 1 # E: Unsupported operand types for + ("str" and "int")
x = x + 'a'
x + 1 # E: Unsupported operand types for + ("str" and "int")
x + 'a'
else:
x = x + 'a' # E: Unsupported operand types for + ("int" and "str")
x = x + 1
x + 'a' # E: Unsupported operand types for + ("int" and "str")
x + 1
[builtins fixtures/isinstancelist.pyi]

[case testIsInstanceIndexing]
Expand Down
22 changes: 11 additions & 11 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -670,45 +670,45 @@ def f(x: str) -> None: pass
[case testTypeCheckNamedModule]
# cmd: mypy -m m.a
[file m/__init__.py]
None + 1
object() + 1
[file m/a.py]
[out]
tmp/m/__init__.py:1: error: Unsupported left operand type for + (None)
tmp/m/__init__.py:1: error: Unsupported left operand type for + ("object")

[case testTypeCheckNamedModule2]
# cmd: mypy -m m.a
[file m/__init__.py]
[file m/a.py]
None + 1
object() + 1
[out]
tmp/m/a.py:1: error: Unsupported left operand type for + (None)
tmp/m/a.py:1: error: Unsupported left operand type for + ("object")

[case testTypeCheckNamedModule3]
# cmd: mypy -m m
[file m/__init__.py]
None + 1
object() + 1
[file m/a.py]
[out]
tmp/m/__init__.py:1: error: Unsupported left operand type for + (None)
tmp/m/__init__.py:1: error: Unsupported left operand type for + ("object")

[case testTypeCheckNamedModule4]
# cmd: mypy -m m
[file m/__init__.py]
[file m/a.py]
None + 1 # Not analyzed.
object() + 1 # Not analyzed.
[out]

[case testTypeCheckNamedModule5]
# cmd: mypy -m m
None + '' # Not analyzed.
object() + 1 # Not analyzed.
[file m.py]
None + 1
object() + 1
[out]
tmp/m.py:1: error: Unsupported left operand type for + (None)
tmp/m.py:1: error: Unsupported left operand type for + ("object")

[case testTypeCheckNamedModuleWithImportCycle]
# cmd: mypy -m m.a
None + 1 # Does not generate error, as this file won't be analyzed.
object() + 1 # Does not generate error, as this file won't be analyzed.
[file m/__init__.py]
import m.a
[file m/a.py]
Expand Down
4 changes: 3 additions & 1 deletion test-data/unit/check-newsyntax.test
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ d['ab'] = 'ab' # E: Invalid index type "str" for Dict[int, str]; expected type

[case testNewSyntaxWithRevealType]
# flags: --python-version 3.6
from typing import Dict
from typing import Dict, TypeVar

T = TypeVar('T')

def tst_local(dct: Dict[int, T]) -> Dict[T, int]:
ret: Dict[T, int] = {}
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-unreachable-code.test
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ main:6: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports"
import typing
MYPY = 0
if MYPY:
None + 1 # E: Unsupported left operand type for + (None)
object() + 1 # E: Unsupported left operand type for + ("object")
else:
None + ''
object() + 1
[builtins fixtures/bool.pyi]

[case testTypeCheckingConditional]
Expand Down