diff --git a/mypy/checker.py b/mypy/checker.py index 0835133fc112..92d806f166cd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1313,6 +1313,8 @@ def split_around_star(self, items: List[T], star_index: int, return (left, star, right) def type_is_iterable(self, type: Type) -> bool: + if isinstance(type, CallableType) and type.is_type_obj(): + type = type.fallback return (is_subtype(type, self.named_generic_type('typing.Iterable', [AnyType()])) and isinstance(type, Instance)) @@ -1817,11 +1819,14 @@ def analyze_iterable_item_type(self, expr: Expression) -> Type: return joined else: # Non-tuple iterable. - self.check_subtype(iterable, - self.named_generic_type('typing.Iterable', - [AnyType()]), - expr, messages.ITERABLE_EXPECTED) - + if isinstance(iterable, CallableType) and iterable.is_type_obj(): + self.check_subtype(iterable.fallback, + self.named_generic_type('typing.Iterable', [AnyType()]), + expr, messages.ITERABLE_EXPECTED) + else: + self.check_subtype(iterable, + self.named_generic_type('typing.Iterable', [AnyType()]), + expr, messages.ITERABLE_EXPECTED) echk = self.expr_checker method = echk.analyze_external_member_access('__iter__', iterable, expr) @@ -1975,8 +1980,7 @@ def visit_yield_from_expr(self, e: YieldFromExpr) -> Type: # by __iter__. if isinstance(subexpr_type, AnyType): iter_type = AnyType() - elif (isinstance(subexpr_type, Instance) and - is_subtype(subexpr_type, self.named_type('typing.Iterable'))): + elif self.type_is_iterable(subexpr_type): if self.is_async_def(subexpr_type) and not self.has_coroutine_decorator(return_type): self.msg.yield_from_invalid_operand_type(subexpr_type, e) iter_method_type = self.expr_checker.analyze_external_member_access( @@ -2243,10 +2247,6 @@ def lookup_typeinfo(self, fullname: str) -> TypeInfo: sym = self.lookup_qualified(fullname) return cast(TypeInfo, sym.node) - def type_type(self) -> Instance: - """Return instance type 'type'.""" - return self.named_type('builtins.type') - def object_type(self) -> Instance: """Return instance type 'object'.""" return self.named_type('builtins.object') diff --git a/mypy/checkmember.py b/mypy/checkmember.py index b57cb57ba016..96974459c6d7 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -176,6 +176,8 @@ def analyze_member_access(name: str, if result: return result fallback = builtin_type('builtins.type') + if item is not None: + fallback = item.type.metaclass_type or fallback return analyze_member_access(name, fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) @@ -438,7 +440,7 @@ def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) -> # Must be an invalid class definition. return AnyType() else: - fallback = builtin_type('builtins.type') + fallback = info.metaclass_type or builtin_type('builtins.type') if init_method.info.fullname() == 'builtins.object': # No non-default __init__ -> look at __new__ instead. new_method = info.get_method('__new__') diff --git a/mypy/fastparse.py b/mypy/fastparse.py index f68d1b0279d0..778401a38993 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -296,6 +296,10 @@ def do_func_def(self, n: Union[ast35.FunctionDef, ast35.AsyncFunctionDef], func_type = None if any(arg_types) or return_type: + if len(arg_types) > len(arg_kinds): + raise FastParserError('Type signature has too many arguments', n.lineno, offset=0) + if len(arg_types) < len(arg_kinds): + raise FastParserError('Type signature has too few arguments', n.lineno, offset=0) func_type = CallableType([a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index 76e95bb4cd55..19af86c57876 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -53,6 +53,7 @@ print('The typed_ast package required by --fast-parser is only compatible with' ' Python 3.3 and greater.') sys.exit(1) +from mypy.fastparse import FastParserError T = TypeVar('T', bound=Union[ast27.expr, ast27.stmt]) U = TypeVar('U', bound=Node) @@ -302,6 +303,10 @@ def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: func_type = None if any(arg_types) or return_type: + if len(arg_types) > len(arg_kinds): + raise FastParserError('Type signature has too many arguments', n.lineno, offset=0) + if len(arg_types) < len(arg_kinds): + raise FastParserError('Type signature has too few arguments', n.lineno, offset=0) func_type = CallableType([a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, diff --git a/mypy/nodes.py b/mypy/nodes.py index e8a6a573087a..97cd18c8dcf0 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1920,6 +1920,29 @@ def __init__(self, names: 'SymbolTable', defn: ClassDef, module_name: str) -> No for vd in defn.type_vars: self.type_vars.append(vd.name) + _metaclass_type = None # type: Optional[mypy.types.Instance] + + @property + def metaclass_type(self) -> 'Optional[mypy.types.Instance]': + if self._metaclass_type: + return self._metaclass_type + if self._fullname == 'builtins.type': + return mypy.types.Instance(self, []) + if self.mro is None: + # XXX why does this happen? + return None + if len(self.mro) > 1: + return self.mro[1].metaclass_type + # FIX: assert False + return None + + @metaclass_type.setter + def metaclass_type(self, value: 'mypy.types.Instance'): + self._metaclass_type = value + + def is_metaclass(self) -> bool: + return self.has_base('builtins.type') + def name(self) -> str: """Short name.""" return self.defn.name diff --git a/mypy/parse.py b/mypy/parse.py index 02aa292ecbbf..f5fa404e3275 100644 --- a/mypy/parse.py +++ b/mypy/parse.py @@ -456,6 +456,10 @@ def parse_function(self, no_type_checks: bool = False) -> FuncDef: else: self.check_argument_kinds(arg_kinds, sig.arg_kinds, def_tok.line, def_tok.column) + if len(sig.arg_types) > len(arg_kinds): + raise ParseError('Type signature has too many arguments') + if len(sig.arg_types) < len(arg_kinds): + raise ParseError('Type signature has too few arguments') typ = CallableType( sig.arg_types, arg_kinds, diff --git a/mypy/semanal.py b/mypy/semanal.py index 9120cd8e9277..d48c1799359a 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -903,7 +903,12 @@ def analyze_metaclass(self, defn: ClassDef) -> None: self.fail("Dynamic metaclass not supported for '%s'" % defn.name, defn) return sym = self.lookup_qualified(defn.metaclass, defn) - if sym is not None and not isinstance(sym.node, TypeInfo): + if sym is not None and isinstance(sym.node, TypeInfo): + inst = fill_typevars(sym.node) + assert isinstance(inst, Instance) + defn.info.metaclass_type = inst + return + else: self.fail("Invalid metaclass '%s'" % defn.metaclass, defn) def object_type(self) -> Instance: diff --git a/mypy/types.py b/mypy/types.py index 9c80b590cd38..40d119c6f55f 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -576,6 +576,7 @@ def __init__(self, ) -> None: if variables is None: variables = [] + assert len(arg_types) == len(arg_kinds) self.arg_types = arg_types self.arg_kinds = arg_kinds self.arg_names = arg_names @@ -624,7 +625,8 @@ def copy_modified(self, ) def is_type_obj(self) -> bool: - return self.fallback.type is not None and self.fallback.type.fullname() == 'builtins.type' + t = self.fallback.type + return t is not None and t.is_metaclass() def is_concrete_type_obj(self) -> bool: return self.is_type_obj() and self.is_classmethod_class diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test index b0f028145716..3aa66d518bda 100644 --- a/test-data/unit/check-fastparse.test +++ b/test-data/unit/check-fastparse.test @@ -160,16 +160,12 @@ main: note: In function "f": def f(): # E: Type signature has too many arguments # type: (int) -> None pass -[out] -main: note: In function "f": [case testFasterParseTooFewArgumentsAnnotation] # flags: --fast-parser def f(x): # E: Type signature has too few arguments # type: () -> None pass -[out] -main: note: In function "f": [case testFasterParseTypeCommentError_python2] # flags: --fast-parser diff --git a/test-data/unit/lib-stub/abc.pyi b/test-data/unit/lib-stub/abc.pyi index 4afe734d29c1..9208f42e9635 100644 --- a/test-data/unit/lib-stub/abc.pyi +++ b/test-data/unit/lib-stub/abc.pyi @@ -1,3 +1,3 @@ -class ABCMeta: pass +class ABCMeta(type): pass abstractmethod = object() abstractproperty = object() diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 2f3e05ea0a4f..ad1fa0884ce9 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -983,13 +983,17 @@ class A(Generic[T], Generic[S]): pass \ [out] [case testInvalidMetaclass] -class A(metaclass=x): pass # E: Name 'x' is not defined +class A(metaclass=x): pass [out] +main:1: error: Name 'x' is not defined +main:1: error: Invalid metaclass 'x' [case testInvalidQualifiedMetaclass] import abc -class A(metaclass=abc.Foo): pass # E: Name 'abc.Foo' is not defined +class A(metaclass=abc.Foo): pass [out] +main:2: error: Name 'abc.Foo' is not defined +main:2: error: Invalid metaclass 'abc.Foo' [case testNonClassMetaclass] def f(): pass