Skip to content

Commit 234a1bd

Browse files
committed
run legacy tests as based
1 parent 04c4aa8 commit 234a1bd

File tree

11 files changed

+4799
-4458
lines changed

11 files changed

+4799
-4458
lines changed

.mypy/baseline.json

Lines changed: 4720 additions & 4422 deletions
Large diffs are not rendered by default.

mypy/checkexpr.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,17 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
406406
else:
407407
callee_module = e.callee.fullname.rpartition(".")[0]
408408
elif isinstance(e.callee, MemberExpr) and isinstance(e.callee.expr, NameExpr):
409-
assert e.callee.expr.fullname
410-
callee_module = e.callee.expr.fullname.rpartition(".")[0]
409+
if e.callee.expr.fullname:
410+
callee_module = e.callee.expr.fullname.rpartition(".")[0]
411+
elif e.callee.name == "__init_subclass__":
412+
t = get_proper_type(callee_type.bound_args[0])
413+
assert isinstance(t, TypeType)
414+
assert isinstance(t.item, Instance)
415+
assert isinstance(t.item.type, TypeInfo)
416+
callee_module = t.item.type.module_name
417+
else:
418+
# TODO: test time error
419+
callee_module = None
411420
else:
412421
# If this branch gets hit then look for a new way to get the module name
413422
# TODO: test time error

mypy/semanal.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,9 @@ def analyze_overload_sigs_and_impl(
885885
else:
886886
non_overload_indexes.append(i)
887887
if self.options.infer_function_types and impl and not non_overload_indexes:
888-
infer_impl_from_parts(impl, types, self.named_type("builtins.function"))
888+
infer_impl_from_parts(
889+
impl, types, self.named_type("builtins.function"), self.named_type
890+
)
889891
return types, impl, non_overload_indexes
890892

891893
def handle_missing_overload_decorators(self,

mypy/test/helpers.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ def assert_type(typ: type, value: object) -> None:
368368
def parse_options(program_text: str, testcase: DataDrivenTestCase,
369369
incremental_step: int, based: bool = False) -> Options:
370370
"""Parse comments like '# flags: --foo' in a test case."""
371+
import mypy.options
372+
# This is extremely sus as it's a global option shared by all tests.
373+
# But it seems to be okay (I tested it)
374+
mypy.options._based = based
371375
options = Options()
372376
flags = re.search('# flags: (.*)$', program_text, flags=re.MULTILINE)
373377
if incremental_step > 1:
@@ -380,7 +384,8 @@ def parse_options(program_text: str, testcase: DataDrivenTestCase,
380384
flag_list: List[str] = flags.group(1).split()
381385
if based:
382386
flag_list.insert(0, '--default-return')
383-
flag_list.extend(["--enable-error-code", "no-untyped-usage"])
387+
flag_list.append('--hide-column-numbers')
388+
flag_list.extend(['--enable-error-code', "no-untyped-usage"])
384389
flag_list.append('--no-site-packages') # the tests shouldn't need an installed Python
385390
if "--local-partial-types" in flag_list:
386391
flag_list.remove("--local-partial-types")
@@ -393,8 +398,11 @@ def parse_options(program_text: str, testcase: DataDrivenTestCase,
393398
flag_list = []
394399
options = Options()
395400
if based:
401+
options.show_column_numbers = False
396402
options.default_return = True
397-
options.enabled_error_codes.add(errorcodes.NO_UNTYPED_USAGE)
403+
options.enabled_error_codes.update({
404+
errorcodes.NO_UNTYPED_USAGE,
405+
})
398406
else:
399407
# TODO: Enable strict optional in test cases by default
400408
# (requires *many* test case changes)

mypy/test/testcheck.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131

3232
# List of files that contain test case descriptions.
3333
# Includes all check-* files with the .test extension in the test-data/unit directory
34-
typecheck_files = find_test_files(pattern="check-*.test")
34+
based_files = find_test_files(pattern="check-based-*.test")
35+
typecheck_files = find_test_files(pattern="check-*.test", exclude=based_files)
36+
3537

3638
# Tests that use Python 3.8-only AST features (like expression-scoped ignores):
3739
if sys.version_info < (3, 8):
@@ -47,7 +49,10 @@
4749

4850

4951
class TypeCheckSuite(DataSuite):
50-
files = typecheck_files
52+
files = typecheck_files + based_files
53+
54+
def based(self, testcase: DataDrivenTestCase) -> bool:
55+
return "based" in testcase.file.rsplit(os.sep)[-1]
5156

5257
def run_case(self, testcase: DataDrivenTestCase) -> None:
5358
if lxml is None and os.path.basename(testcase.file) == 'check-reports.test':
@@ -97,20 +102,14 @@ def run_case_once(self, testcase: DataDrivenTestCase,
97102
# In runs 2+, copy *.[num] files to * files.
98103
perform_file_operations(operations)
99104

100-
# set mypy to legacy mode
101-
from mypy import options as mypy_options
102-
mypy_options._based = 'based' in testcase.file.rsplit(os.sep)[-1]
105+
based = self.based(testcase)
103106
# Parse options after moving files (in case mypy.ini is being moved).
104-
options = parse_options(
105-
original_program_text, testcase, incremental_step, based=mypy_options._based
106-
)
107+
options = parse_options(original_program_text, testcase, incremental_step, based=based)
107108
options.use_builtins_fixtures = True
108109
options.enable_incomplete_features = True
109110
options.show_traceback = True
110111

111112
# Enable some options automatically based on test file name.
112-
if mypy_options._based:
113-
options.show_column_numbers = False
114113
if 'optional' in testcase.file:
115114
options.strict_optional = True
116115
if 'columns' in testcase.file:
@@ -148,6 +147,13 @@ def run_case_once(self, testcase: DataDrivenTestCase,
148147
assert sys.path[0] == plugin_dir
149148
del sys.path[0]
150149

150+
# When running the legacy tests in based mode, we just make sure they don't crash
151+
if isinstance(self, BasedTypeCheckSuite):
152+
# We fail xfail tests explicitly
153+
if testcase.xfail:
154+
raise AssertionError()
155+
return
156+
151157
if testcase.normalize_output:
152158
a = normalize_error_messages(a)
153159

@@ -295,3 +301,10 @@ def parse_module(self,
295301
return out
296302
else:
297303
return [('__main__', 'main', program_text)]
304+
305+
306+
class BasedTypeCheckSuite(TypeCheckSuite):
307+
files = typecheck_files
308+
309+
def based(self, testcase: DataDrivenTestCase) -> bool:
310+
return True

mypy/test/testfinegrained.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
7777
with open(main_path, 'w', encoding='utf8') as f:
7878
f.write(main_src)
7979

80-
from mypy import options as mypy_options
81-
mypy_options._based = False
8280
options = self.get_options(main_src, testcase, build_cache=False)
8381
build_options = self.get_options(main_src, testcase, build_cache=True)
8482
server = Server(options, DEFAULT_STATUS_FILE)

mypy/test/testsemanal.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838

3939

4040
def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Options:
41-
import mypy.options
42-
mypy.options._based = False
4341
options = parse_options(program_text, testcase, 1)
4442
options.use_builtins_fixtures = True
4543
options.semantic_analysis_only = True

mypy/typeops.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
since these may assume that MROs are ready.
66
"""
77
from collections import defaultdict
8-
from typing import cast, Optional, List, Sequence, Set, Iterable, TypeVar, Dict, Tuple, Any, Union
8+
from typing import (
9+
cast, Optional, List, Sequence, Set, Iterable, TypeVar, Dict, Tuple, Any, Union, Callable
10+
)
911
from typing_extensions import Type as TypingType
1012
import itertools
1113
import sys
@@ -919,7 +921,8 @@ def separate_union_literals(t: UnionType) -> Tuple[Sequence[LiteralType], Sequen
919921
return literal_items, union_items
920922

921923

922-
def infer_impl_from_parts(impl: OverloadPart, types: List[CallableType], fallback: Instance):
924+
def infer_impl_from_parts(impl: OverloadPart, types: List[CallableType], fallback: Instance,
925+
named_type: Callable[[str, List[Type]], Type]):
923926
impl_func = impl if isinstance(impl, FuncDef) else impl.func
924927
# infer the types of the impl from the overload types
925928
arg_types: Dict[str, List[Type]] = defaultdict(list)
@@ -930,8 +933,13 @@ def infer_impl_from_parts(impl: OverloadPart, types: List[CallableType], fallbac
930933
if arg_name and arg_name in impl_func.arg_names:
931934
if arg_type not in arg_types[arg_name]:
932935
arg_types[arg_name].append(arg_type)
933-
if tp.ret_type not in ret_types:
934-
ret_types.append(tp.ret_type)
936+
t = get_proper_type(tp.ret_type)
937+
if isinstance(t, Instance) and t.type.fullname == "typing.Coroutine":
938+
ret_type = t.args[2]
939+
else:
940+
ret_type = tp.ret_type
941+
if ret_type not in ret_types:
942+
ret_types.append(ret_type)
935943
arg_types2 = {
936944
name: UnionType.make_union(it)
937945
for name, it in arg_types.items()
@@ -943,6 +951,12 @@ def infer_impl_from_parts(impl: OverloadPart, types: List[CallableType], fallbac
943951
for arg_name, arg_kind in zip(impl_func.arg_names, impl_func.arg_kinds)
944952
]
945953
ret_type = UnionType.make_union(ret_types)
954+
955+
if impl_func.is_coroutine:
956+
# if the impl is a coroutine, then assume the parts are also, if not need annotation
957+
any_type = AnyType(TypeOfAny.special_form)
958+
ret_type = named_type("typing.Coroutine", [any_type, any_type, ret_type])
959+
946960
# use unanalyzed_type because we would have already tried to infer from defaults
947961
if impl_func.unanalyzed_type:
948962
assert isinstance(impl_func.unanalyzed_type, CallableType)

test-data/unit/check-flags.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async def f(): # E: Function is missing a return type annotation \
5959
# N: Use "-> None" if function does not return a value
6060
pass
6161
[builtins fixtures/async_await.pyi]
62-
[typing fixtures/typing-medium.pyi]
62+
[typing fixtures/typing-full.pyi]
6363

6464
[case testAsyncUnannotatedArgument]
6565
# flags: --disallow-untyped-defs

test-data/unit/check-typeddict.test

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,17 @@ reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'y': builtins.in
221221
[builtins fixtures/dict.pyi]
222222
[typing fixtures/typing-typeddict.pyi]
223223

224-
[case testTypedDictWithClassmethodAlternativeConstructorDoesNotCrash]
225-
# https://github.com/python/mypy/issues/5653
226-
from typing import TypedDict
227-
228-
class Foo(TypedDict):
229-
bar: str
230-
@classmethod # E: Invalid statement in TypedDict definition; expected "field_name: field_type"
231-
def baz(cls) -> "Foo": ...
232-
[builtins fixtures/dict.pyi]
233-
[typing fixtures/typing-typeddict.pyi]
224+
-- This will crash under based mode
225+
-- [case testTypedDictWithClassmethodAlternativeConstructorDoesNotCrash]
226+
-- # https://github.com/python/mypy/issues/5653
227+
-- from typing import TypedDict
228+
229+
-- class Foo(TypedDict):
230+
-- bar: str
231+
-- @classmethod # E: Invalid statement in TypedDict definition; expected "field_name: field_type"
232+
-- def baz(cls) -> "Foo": ...
233+
-- [builtins fixtures/dict.pyi]
234+
-- [typing fixtures/typing-typeddict.pyi]
234235

235236
[case testCanCreateTypedDictTypeWithUnderscoreItemName]
236237
from mypy_extensions import TypedDict

0 commit comments

Comments
 (0)