From 29848f8c607f90e0100eeef09747d47095c66ff1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Mon, 22 May 2017 16:15:06 -0700 Subject: [PATCH 01/14] Add flag to disallow implicit Any types. These types can appear from an unanalyzed module. If mypy encounters a type annotation that uses such a type, it will report an error. Fixes #3205 --- mypy/build.py | 4 +-- mypy/checker.py | 17 ++++++++++++ mypy/main.py | 3 ++ mypy/options.py | 5 ++++ mypy/semanal.py | 22 +++++++++++++-- mypy/typeanal.py | 21 +++++++++++++- mypy/types.py | 3 ++ test-data/unit/check-flags.test | 49 +++++++++++++++++++++++++++++++++ 8 files changed, 117 insertions(+), 7 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index f803929a8fdfd..16a4451202a28 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1387,9 +1387,7 @@ def fix_suppressed_dependencies(self, graph: Graph) -> None: """ # TODO: See if it's possible to move this check directly into parse_file in some way. # TODO: Find a way to write a test case for this fix. - silent_mode = (self.options.ignore_missing_imports or - self.options.follow_imports == 'skip') - if not silent_mode: + if not self.options.silent_mode(): return new_suppressed = [] diff --git a/mypy/checker.py b/mypy/checker.py index 870c561852b64..fe9472322c27a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -29,6 +29,7 @@ ARG_POS, MDEF, CONTRAVARIANT, COVARIANT) from mypy import nodes +from mypy.typeanal import has_any_from_silent_import from mypy.types import ( Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneTyp, strip_type, TypeType, @@ -611,6 +612,18 @@ def is_implicit_any(t: Type) -> bool: self.fail(messages.RETURN_TYPE_EXPECTED, fdef) if any(is_implicit_any(t) for t in fdef.type.arg_types): self.fail(messages.ARGUMENT_TYPE_EXPECTED, fdef) + if self.options.disallow_implicit_any_types: + if fdef.type and isinstance(fdef.type, CallableType): + if has_any_from_silent_import(fdef.type.ret_type): + self.fail("Return type is implicitly converted to " + "'{}' due to import from " + "unanalyzed module".format(fdef.type.ret_type), fdef) + for idx, arg_type in enumerate(fdef.type.arg_types): + if has_any_from_silent_import(arg_type): + self.fail("Argument {} to '{}' is implicitly converted to " + "'{}' due to import from unanalyzed " + "module".format(idx + 1, fdef.name(), arg_type), + fdef) if name in nodes.reverse_op_method_set: self.check_reverse_op_method(item, typ, name) @@ -1706,6 +1719,10 @@ def check_simple_assignment(self, lvalue_type: Type, rvalue: Expression, self.msg.deleted_as_rvalue(rvalue_type, context) if isinstance(lvalue_type, DeletedType): self.msg.deleted_as_lvalue(lvalue_type, context) + elif (self.options.disallow_implicit_any_types + and has_any_from_silent_import(lvalue_type)): + self.msg.fail("Type of {} is implicitly converted to '{}' due to import from " + "unanalyzed module".format(lvalue_name, lvalue_type), context) else: self.check_subtype(rvalue_type, lvalue_type, context, msg, '{} has type'.format(rvalue_name), diff --git a/mypy/main.py b/mypy/main.py index a5511671c966b..cc0ad6bb0cf55 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -222,6 +222,9 @@ def add_invertible_flag(flag: str, help="silently ignore imports of missing modules") parser.add_argument('--follow-imports', choices=['normal', 'silent', 'skip', 'error'], default='normal', help="how to treat imports (default normal)") + parser.add_argument('--disallow-implicit-any-types', action='store_true', + help="disallow implicit conversion of types from unanalyzed modules" + " into Any") add_invertible_flag('--disallow-untyped-calls', default=False, strict_flag=True, help="disallow calling functions without type annotations" " from functions with type annotations") diff --git a/mypy/options.py b/mypy/options.py index 8c8764200800a..697df0cf82a53 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -19,6 +19,7 @@ class Options: PER_MODULE_OPTIONS = { "ignore_missing_imports", "follow_imports", + "disallow_implicit_any_types", "disallow_untyped_calls", "disallow_untyped_defs", "check_untyped_defs", @@ -44,6 +45,7 @@ def __init__(self) -> None: self.report_dirs = {} # type: Dict[str, str] self.ignore_missing_imports = False self.follow_imports = 'normal' # normal|silent|skip|error + self.disallow_implicit_any_types = False # Disallow calling untyped functions from typed ones self.disallow_untyped_calls = False @@ -158,3 +160,6 @@ def module_matches_pattern(self, module: str, pattern: Pattern[str]) -> bool: def select_options_affecting_cache(self) -> Mapping[str, bool]: return {opt: getattr(self, opt) for opt in self.OPTIONS_AFFECTING_CACHE} + + def silent_mode(self) -> bool: + return self.ignore_missing_imports or self.follow_imports == 'skip' diff --git a/mypy/semanal.py b/mypy/semanal.py index b14e3dd9076eb..7b6bbe102dc2a 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -85,7 +85,7 @@ from mypy.nodes import implicit_module_attrs from mypy.typeanal import ( TypeAnalyser, TypeAnalyserPass3, analyze_type_alias, no_subscript_builtin_alias, - TypeVariableQuery, TypeVarList, remove_dups, + TypeVariableQuery, TypeVarList, remove_dups, has_any_from_silent_import ) from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type @@ -964,7 +964,9 @@ def analyze_base_classes(self, defn: ClassDef) -> None: self.fail("Cannot subclass NewType", defn) base_types.append(base) elif isinstance(base, AnyType): - if self.options.disallow_subclassing_any: + # if --disallow-implicit-any-types is set, the issue is reported later + if (self.options.disallow_subclassing_any and + not self.options.disallow_implicit_any_types): if isinstance(base_expr, (NameExpr, MemberExpr)): msg = "Class cannot subclass '{}' (has type 'Any')".format(base_expr.name) else: @@ -974,6 +976,15 @@ def analyze_base_classes(self, defn: ClassDef) -> None: else: self.fail('Invalid base class', base_expr) info.fallback_to_any = True + if (self.options.disallow_implicit_any_types and + has_any_from_silent_import(base)): + if isinstance(base_expr, (NameExpr, MemberExpr)): + msg = ("Subclassing type '{}' that is implicitly converted to '{}' due to " + "import from unanalyzed module".format(base_expr.name, base)) + else: + msg = ("Subclassing a type that is implicitly converted to '{}' " + "due to import from unanalyzed module".format(base)) + self.fail(msg, base_expr) # Add 'object' as implicit base if there is no other base class. if (not base_types and defn.fullname != 'builtins.object'): @@ -1428,7 +1439,12 @@ def add_unknown_symbol(self, name: str, context: Context, is_import: bool = Fals else: var._fullname = self.qualified_name(name) var.is_ready = True - var.type = AnyType() + any_type = AnyType() + if self.options.silent_mode(): + # if silent mode is not enabled, no need to report implicit conversion to Any, + # let mypy report an import error. + any_type.is_from_silent_import = is_import + var.type = any_type var.is_suppressed_import = is_import self.add_symbol(name, SymbolTableNode(GDEF, var, self.cur_mod_id), context) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 2a506b7a53786..458d71bbe2140 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -232,7 +232,9 @@ def visit_unbound_type(self, t: UnboundType) -> Type: # context. This is slightly problematic as it allows using the type 'Any' # as a base class -- however, this will fail soon at runtime so the problem # is pretty minor. - return AnyType() + any_type = AnyType() + any_type.is_from_silent_import = sym.node.type.is_from_silent_import + return any_type # Allow unbound type variables when defining an alias if not (self.aliasing and sym.kind == TVAR and self.tvar_scope.get_binding(sym) is None): @@ -731,6 +733,23 @@ def visit_callable_type(self, t: CallableType) -> TypeVarList: return [] +def has_any_from_silent_import(t: Type) -> bool: + """Return true if this type was converted to Any because of a silenced import. + + If type t is was co or is has type arguments that contain such Any type + this function will return true. + """ + return t.accept(HasAnyFromSilentImportQuery()) + + +class HasAnyFromSilentImportQuery(TypeQuery[bool]): + def __init__(self) -> None: + super().__init__(any) + + def visit_any(self, t: AnyType) -> bool: + return t.is_from_silent_import + + def make_optional_type(t: Type) -> Type: """Return the type corresponding to Optional[t]. diff --git a/mypy/types.py b/mypy/types.py index c17bb063f5fb1..37c75be2f0d3e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -272,6 +272,9 @@ def serialize(self) -> JsonDict: class AnyType(Type): """The type 'Any'.""" + # Does this come from a silent import? Used for --disallow-implicit-any-types flag + is_from_silent_import = False + def __init__(self, implicit: bool = False, line: int = -1, column: int = -1) -> None: super().__init__(line, column) self.implicit = implicit diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 2b0e5549589a2..fe770b933a3ef 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -376,3 +376,52 @@ if y: # E: Condition must be a boolean if z: # E: Condition must be a boolean pass [builtins fixtures/bool.pyi] + +[case testDisallowImplicitTypesIgnoreMissingTypes] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import MyType + +def f(x: MyType) -> None: # E: Argument 1 to 'f' is implicitly converted to 'Any' due to import from unanalyzed module + pass + +[case testDisallowImplicitTypes] +# flags: --disallow-implicit-any-types +from missing import MyType + +def f(x: MyType) -> None: + pass +[out] +main:2: error: Cannot find module named 'missing' +main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) + +[case testDisallowImplicitAnyVariableDefinition] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import Unchecked + +t: Unchecked = 12 # E: Type of variable is implicitly converted to 'Any' due to import from unanalyzed module + +[case testDisallowImplicitAnyGeneric] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import Unchecked +from typing import List + +def foo(l: List[Unchecked]) -> List[Unchecked]: + t = [] # type: List[Unchecked] + return l +[builtins fixtures/list.pyi] +[out] +main:5: error: Return type is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module +main:5: error: Argument 1 to 'foo' is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module +main:6: error: Type of variable is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module + +[case testDisallowImplicitAnyInherit] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import Unchecked +from typing import List + +class C(Unchecked): # E: Subclassing type 'Unchecked' that is implicitly converted to 'Any' due to import from unanalyzed module + pass + +class A(List[Unchecked]): # E: Subclassing a type that is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module + pass +[builtins fixtures/list.pyi] From e93f7aaca1eea79213b47732c5a778ea1db1e84c Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Tue, 23 May 2017 15:07:19 -0700 Subject: [PATCH 02/14] Update error messages to be more user-friendly --- mypy/checker.py | 20 +++++++++++------- mypy/semanal.py | 12 ++++++----- mypy/typeanal.py | 2 +- test-data/unit/check-flags.test | 37 +++++++++++++++++++++------------ typeshed | 2 +- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index fe9472322c27a..961653ffc4682 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -614,15 +614,18 @@ def is_implicit_any(t: Type) -> bool: self.fail(messages.ARGUMENT_TYPE_EXPECTED, fdef) if self.options.disallow_implicit_any_types: if fdef.type and isinstance(fdef.type, CallableType): - if has_any_from_silent_import(fdef.type.ret_type): + ret_type = fdef.type.ret_type + if has_any_from_silent_import(ret_type): + type_name = self.msg.format(ret_type) self.fail("Return type is implicitly converted to " - "'{}' due to import from " - "unanalyzed module".format(fdef.type.ret_type), fdef) + "{} due to import from " + "unanalyzed module".format(type_name), fdef) for idx, arg_type in enumerate(fdef.type.arg_types): if has_any_from_silent_import(arg_type): - self.fail("Argument {} to '{}' is implicitly converted to " - "'{}' due to import from unanalyzed " - "module".format(idx + 1, fdef.name(), arg_type), + arg_type_name = self.msg.format(arg_type) + self.fail("Argument {} to \"{}\" is implicitly converted to " + "{} due to import from unanalyzed " + "module".format(idx + 1, fdef.name(), arg_type_name), fdef) if name in nodes.reverse_op_method_set: @@ -1721,8 +1724,9 @@ def check_simple_assignment(self, lvalue_type: Type, rvalue: Expression, self.msg.deleted_as_lvalue(lvalue_type, context) elif (self.options.disallow_implicit_any_types and has_any_from_silent_import(lvalue_type)): - self.msg.fail("Type of {} is implicitly converted to '{}' due to import from " - "unanalyzed module".format(lvalue_name, lvalue_type), context) + type_name = self.msg.format(lvalue_type) + self.msg.fail("Type of {} is implicitly converted to {} due to import from " + "unanalyzed module".format(lvalue_name, type_name), context) else: self.check_subtype(rvalue_type, lvalue_type, context, msg, '{} has type'.format(rvalue_name), diff --git a/mypy/semanal.py b/mypy/semanal.py index 7b6bbe102dc2a..aadd56ffbed29 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -75,7 +75,7 @@ from mypy.visitor import NodeVisitor from mypy.traverser import TraverserVisitor from mypy.errors import Errors, report_internal_error -from mypy.messages import CANNOT_ASSIGN_TO_TYPE +from mypy.messages import CANNOT_ASSIGN_TO_TYPE, MessageBuilder from mypy.types import ( NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, FunctionLike, UnboundType, TypeList, TypeVarDef, TypeType, @@ -236,6 +236,7 @@ def __init__(self, self.lib_path = lib_path self.errors = errors self.modules = modules + self.msg = MessageBuilder(errors, modules) self.missing_modules = missing_modules self.postpone_nested_functions_stack = [FUNCTION_BOTH_PHASES] self.postponed_functions_stack = [] @@ -978,12 +979,13 @@ def analyze_base_classes(self, defn: ClassDef) -> None: info.fallback_to_any = True if (self.options.disallow_implicit_any_types and has_any_from_silent_import(base)): + base_name = self.msg.format(base) if isinstance(base_expr, (NameExpr, MemberExpr)): - msg = ("Subclassing type '{}' that is implicitly converted to '{}' due to " - "import from unanalyzed module".format(base_expr.name, base)) + msg = ("Subclassing type {} that is implicitly converted to {} due to " + "import from unanalyzed module".format(base_expr.name, base_name)) else: - msg = ("Subclassing a type that is implicitly converted to '{}' " - "due to import from unanalyzed module".format(base)) + msg = ("Subclassing a type that is implicitly converted to {} " + "due to import from unanalyzed module".format(base_name)) self.fail(msg, base_expr) # Add 'object' as implicit base if there is no other base class. diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 458d71bbe2140..edaeaae58f834 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -736,7 +736,7 @@ def visit_callable_type(self, t: CallableType) -> TypeVarList: def has_any_from_silent_import(t: Type) -> bool: """Return true if this type was converted to Any because of a silenced import. - If type t is was co or is has type arguments that contain such Any type + If type t is such Any type or has type arguments that contain such Any type this function will return true. """ return t.accept(HasAnyFromSilentImportQuery()) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index fe770b933a3ef..070aadac0154d 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -381,15 +381,15 @@ if z: # E: Condition must be a boolean # flags: --ignore-missing-imports --disallow-implicit-any-types from missing import MyType -def f(x: MyType) -> None: # E: Argument 1 to 'f' is implicitly converted to 'Any' due to import from unanalyzed module - pass +def f(x: MyType) -> None: # E: Argument 1 to "f" is implicitly converted to "Any" due to import from unanalyzed module + pass [case testDisallowImplicitTypes] # flags: --disallow-implicit-any-types from missing import MyType def f(x: MyType) -> None: - pass + pass [out] main:2: error: Cannot find module named 'missing' main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) @@ -398,7 +398,7 @@ main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" # flags: --ignore-missing-imports --disallow-implicit-any-types from missing import Unchecked -t: Unchecked = 12 # E: Type of variable is implicitly converted to 'Any' due to import from unanalyzed module +t: Unchecked = 12 # E: Type of variable is implicitly converted to "Any" due to import from unanalyzed module [case testDisallowImplicitAnyGeneric] # flags: --ignore-missing-imports --disallow-implicit-any-types @@ -406,22 +406,33 @@ from missing import Unchecked from typing import List def foo(l: List[Unchecked]) -> List[Unchecked]: - t = [] # type: List[Unchecked] - return l + t = [] # type: List[Unchecked] + return l [builtins fixtures/list.pyi] [out] -main:5: error: Return type is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module -main:5: error: Argument 1 to 'foo' is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module -main:6: error: Type of variable is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module +main:5: error: Return type is implicitly converted to List[Any] due to import from unanalyzed module +main:5: error: Argument 1 to "foo" is implicitly converted to List[Any] due to import from unanalyzed module +main:6: error: Type of variable is implicitly converted to List[Any] due to import from unanalyzed module [case testDisallowImplicitAnyInherit] # flags: --ignore-missing-imports --disallow-implicit-any-types from missing import Unchecked from typing import List -class C(Unchecked): # E: Subclassing type 'Unchecked' that is implicitly converted to 'Any' due to import from unanalyzed module - pass +class C(Unchecked): # E: Subclassing type Unchecked that is implicitly converted to "Any" due to import from unanalyzed module + pass -class A(List[Unchecked]): # E: Subclassing a type that is implicitly converted to 'builtins.list[Any]' due to import from unanalyzed module - pass +class A(List[Unchecked]): # E: Subclassing a type that is implicitly converted to List[Any] due to import from unanalyzed module + pass +[builtins fixtures/list.pyi] + +[case testDisallowImplicitAnyAlias] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import Unchecked +from typing import List + +X = List[Unchecked] + +def f(x: X) -> None: # E: Argument 1 to "f" is implicitly converted to List[Any] due to import from unanalyzed module + pass [builtins fixtures/list.pyi] diff --git a/typeshed b/typeshed index c2e6a6f670f5d..85d017d1e8ac4 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit c2e6a6f670f5d75c07eb31cf2509db302825b76d +Subproject commit 85d017d1e8ac4b86ece84a5ea44720d295d1dd61 From 001d4c27f1fd9bd09428da4c12b5b3027dd4dfbc Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Tue, 23 May 2017 16:56:52 -0700 Subject: [PATCH 03/14] Expand error checking on several more cases --- mypy/checker.py | 20 +++++------- mypy/checkexpr.py | 11 ++++++- mypy/messages.py | 4 +++ mypy/semanal.py | 19 +++++++---- test-data/unit/check-flags.test | 56 +++++++++++++++++++++++++++++++-- 5 files changed, 88 insertions(+), 22 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 961653ffc4682..ea45a2810627b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -616,18 +616,13 @@ def is_implicit_any(t: Type) -> bool: if fdef.type and isinstance(fdef.type, CallableType): ret_type = fdef.type.ret_type if has_any_from_silent_import(ret_type): - type_name = self.msg.format(ret_type) - self.fail("Return type is implicitly converted to " - "{} due to import from " - "unanalyzed module".format(type_name), fdef) + self.msg.implicit_any_from_silent_import("Return type", ret_type, + fdef) for idx, arg_type in enumerate(fdef.type.arg_types): if has_any_from_silent_import(arg_type): - arg_type_name = self.msg.format(arg_type) - self.fail("Argument {} to \"{}\" is implicitly converted to " - "{} due to import from unanalyzed " - "module".format(idx + 1, fdef.name(), arg_type_name), - fdef) - + prefix = "Argument {} to \"{}\"".format(idx + 1, fdef.name()) + self.msg.implicit_any_from_silent_import(prefix, arg_type, + fdef) if name in nodes.reverse_op_method_set: self.check_reverse_op_method(item, typ, name) elif name in ('__getattr__', '__getattribute__'): @@ -1724,9 +1719,8 @@ def check_simple_assignment(self, lvalue_type: Type, rvalue: Expression, self.msg.deleted_as_lvalue(lvalue_type, context) elif (self.options.disallow_implicit_any_types and has_any_from_silent_import(lvalue_type)): - type_name = self.msg.format(lvalue_type) - self.msg.fail("Type of {} is implicitly converted to {} due to import from " - "unanalyzed module".format(lvalue_name, type_name), context) + prefix = "Type of {}".format(lvalue_name) + self.msg.implicit_any_from_silent_import(prefix, lvalue_type, context) else: self.check_subtype(rvalue_type, lvalue_type, context, msg, '{} has type'.format(rvalue_name), diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index b19a0a1bcfd26..7ac5990fb3bbf 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4,6 +4,7 @@ from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional from mypy.errors import report_internal_error +from mypy.typeanal import has_any_from_silent_import from mypy.types import ( Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef, TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType, @@ -1543,8 +1544,11 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: """Type check a cast expression.""" source_type = self.accept(expr.expr, type_context=AnyType(), allow_none_return=True) target_type = expr.type - if self.chk.options.warn_redundant_casts and is_same_type(source_type, target_type): + options = self.chk.options + if options.warn_redundant_casts and is_same_type(source_type, target_type): self.msg.redundant_cast(target_type, expr) + if options.disallow_implicit_any_types and has_any_from_silent_import(target_type): + self.msg.implicit_any_from_silent_import("Target type of cast", target_type, expr) return target_type def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type: @@ -2229,6 +2233,11 @@ def visit_newtype_expr(self, e: NewTypeExpr) -> Type: return AnyType() def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type: + tuple_type = e.info.tuple_type + if tuple_type: + if (self.chk.options.disallow_implicit_any_types and + has_any_from_silent_import(tuple_type)): + self.msg.implicit_any_from_silent_import("NamedTuple type", tuple_type, e) # TODO: Perhaps return a type object type? return AnyType() diff --git a/mypy/messages.py b/mypy/messages.py index 5c2f5d16fdc72..ddd92bb360f81 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -855,6 +855,10 @@ def unsupported_type_type(self, item: Type, context: Context) -> None: def redundant_cast(self, typ: Type, context: Context) -> None: self.note('Redundant cast to {}'.format(self.format(typ)), context) + def implicit_any_from_silent_import(self, prefix: str, typ: Type, ctx: Context) -> None: + self.fail("{} is implicitly converted to {} due to import from unanalyzed module".format( + prefix, self.format(typ)), ctx) + def typeddict_instantiated_with_unexpected_items(self, expected_item_names: List[str], actual_item_names: List[str], diff --git a/mypy/semanal.py b/mypy/semanal.py index aadd56ffbed29..539203b664709 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -979,14 +979,11 @@ def analyze_base_classes(self, defn: ClassDef) -> None: info.fallback_to_any = True if (self.options.disallow_implicit_any_types and has_any_from_silent_import(base)): - base_name = self.msg.format(base) if isinstance(base_expr, (NameExpr, MemberExpr)): - msg = ("Subclassing type {} that is implicitly converted to {} due to " - "import from unanalyzed module".format(base_expr.name, base_name)) + prefix = "Base type {}".format(base_expr.name) else: - msg = ("Subclassing a type that is implicitly converted to {} " - "due to import from unanalyzed module".format(base_name)) - self.fail(msg, base_expr) + prefix = "Base type" + self.msg.implicit_any_from_silent_import(prefix, base, base_expr) # Add 'object' as implicit base if there is no other base class. if (not base_types and defn.fullname != 'builtins.object'): @@ -1893,6 +1890,16 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None: return variance, upper_bound = res + if self.options.disallow_implicit_any_types: + for idx, constraint in enumerate(values): + if has_any_from_silent_import(constraint): + prefix = "Constraint {}".format(idx + 1) + self.msg.implicit_any_from_silent_import(prefix, constraint, s) + + if has_any_from_silent_import(upper_bound): + prefix = "Upper bound of type variable" + self.msg.implicit_any_from_silent_import(prefix, upper_bound, s) + # Yes, it's a valid type variable definition! Add it to the symbol table. node = self.lookup(name, s) node.kind = TVAR diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 070aadac0154d..0964abba1be37 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -419,10 +419,10 @@ main:6: error: Type of variable is implicitly converted to List[Any] due to impo from missing import Unchecked from typing import List -class C(Unchecked): # E: Subclassing type Unchecked that is implicitly converted to "Any" due to import from unanalyzed module +class C(Unchecked): # E: Base type Unchecked is implicitly converted to "Any" due to import from unanalyzed module pass -class A(List[Unchecked]): # E: Subclassing a type that is implicitly converted to List[Any] due to import from unanalyzed module +class A(List[Unchecked]): # E: Base type is implicitly converted to List[Any] due to import from unanalyzed module pass [builtins fixtures/list.pyi] @@ -436,3 +436,55 @@ X = List[Unchecked] def f(x: X) -> None: # E: Argument 1 to "f" is implicitly converted to List[Any] due to import from unanalyzed module pass [builtins fixtures/list.pyi] +[case testDisallowImplicitAnyCast] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import Unchecked +from typing import List, cast + + +foo = [1, 2, 3] +cast(List[Unchecked], foo) # E: Target type of cast is implicitly converted to List[Any] due to import from unanalyzed module +cast(Unchecked, foo) # E: Target type of cast is implicitly converted to "Any" due to import from unanalyzed module +[builtins fixtures/list.pyi] + +[case testDisallowImplicitAnyNamedTuple] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from typing import List, NamedTuple +from missing import Unchecked + +Point = NamedTuple('Point', [('x', List[Unchecked]), + ('y', Unchecked)]) +[builtins fixtures/list.pyi] +[out] +main:5: error: NamedTuple type is implicitly converted to "Tuple[List[Any], Any]" due to import from unanalyzed module + +[case testDisallowImplicitAnyTypeVarConstraints] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from typing import List, NamedTuple, TypeVar, Any +from missing import Unchecked + +T = TypeVar('T', Unchecked, List[Unchecked], str) +[builtins fixtures/list.pyi] +[out] +main:5: error: Constraint 1 is implicitly converted to "Any" due to import from unanalyzed module +main:5: error: Constraint 2 is implicitly converted to List[Any] due to import from unanalyzed module + +[case testDisallowImplicitAnyNewType] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from typing import NewType +from missing import Unchecked + +Baz = NewType('Baz', Unchecked) +[out] +main:5: error: Argument 2 to NewType(...) must be subclassable (got Any) +[case testDisallowImplicitAnyCallableAndTuple] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from typing import Callable, Tuple +from missing import Unchecked + +def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]: + return f() +[builtins fixtures/list.pyi] +[out] +main:5: error: Return type is implicitly converted to "Tuple[Any]" due to import from unanalyzed module +main:5: error: Argument 1 to "foo" is implicitly converted to Callable[[], Any] due to import from unanalyzed module From 3b05b9c0b491d114170f896e0bf104aef3458fcb Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Tue, 23 May 2017 22:34:31 -0700 Subject: [PATCH 04/14] Fix issue with subclassing explicit Any not being reported --- mypy/semanal.py | 4 ++-- test-data/unit/check-flags.test | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 539203b664709..474022a71cce8 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -966,8 +966,8 @@ def analyze_base_classes(self, defn: ClassDef) -> None: base_types.append(base) elif isinstance(base, AnyType): # if --disallow-implicit-any-types is set, the issue is reported later - if (self.options.disallow_subclassing_any and - not self.options.disallow_implicit_any_types): + implicit = self.options.disallow_implicit_any_types and base.is_from_silent_import + if self.options.disallow_subclassing_any and not implicit: if isinstance(base_expr, (NameExpr, MemberExpr)): msg = "Class cannot subclass '{}' (has type 'Any')".format(base_expr.name) else: diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 0964abba1be37..52a395297c41d 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -488,3 +488,9 @@ def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]: [out] main:5: error: Return type is implicitly converted to "Tuple[Any]" due to import from unanalyzed module main:5: error: Argument 1 to "foo" is implicitly converted to Callable[[], Any] due to import from unanalyzed module +[case testDisallowImplicitAnySubclassingExplicitAny] +# flags: --ignore-missing-imports --disallow-implicit-any-types --disallow-subclassing-any +from typing import Any + +class C(Any): # E: Class cannot subclass 'Any' (has type 'Any') + pass From f3ce4e635b51db326ae97c01fa87c9bdca0e61b4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 25 May 2017 12:19:38 -0700 Subject: [PATCH 05/14] Address code review comments from David --- mypy/semanal.py | 11 ++--------- mypy/typeanal.py | 6 ++---- mypy/types.py | 10 +++++----- test-data/unit/check-flags.test | 1 + 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 474022a71cce8..c4584c7eeb508 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -965,9 +965,7 @@ def analyze_base_classes(self, defn: ClassDef) -> None: self.fail("Cannot subclass NewType", defn) base_types.append(base) elif isinstance(base, AnyType): - # if --disallow-implicit-any-types is set, the issue is reported later - implicit = self.options.disallow_implicit_any_types and base.is_from_silent_import - if self.options.disallow_subclassing_any and not implicit: + if self.options.disallow_subclassing_any: if isinstance(base_expr, (NameExpr, MemberExpr)): msg = "Class cannot subclass '{}' (has type 'Any')".format(base_expr.name) else: @@ -1438,12 +1436,7 @@ def add_unknown_symbol(self, name: str, context: Context, is_import: bool = Fals else: var._fullname = self.qualified_name(name) var.is_ready = True - any_type = AnyType() - if self.options.silent_mode(): - # if silent mode is not enabled, no need to report implicit conversion to Any, - # let mypy report an import error. - any_type.is_from_silent_import = is_import - var.type = any_type + var.type = AnyType(from_silent_import=is_import) var.is_suppressed_import = is_import self.add_symbol(name, SymbolTableNode(GDEF, var, self.cur_mod_id), context) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index edaeaae58f834..0a446e4c86487 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -232,9 +232,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type: # context. This is slightly problematic as it allows using the type 'Any' # as a base class -- however, this will fail soon at runtime so the problem # is pretty minor. - any_type = AnyType() - any_type.is_from_silent_import = sym.node.type.is_from_silent_import - return any_type + return AnyType(from_silent_import=True) # Allow unbound type variables when defining an alias if not (self.aliasing and sym.kind == TVAR and self.tvar_scope.get_binding(sym) is None): @@ -747,7 +745,7 @@ def __init__(self) -> None: super().__init__(any) def visit_any(self, t: AnyType) -> bool: - return t.is_from_silent_import + return t.from_silent_import def make_optional_type(t: Type) -> Type: diff --git a/mypy/types.py b/mypy/types.py index 37c75be2f0d3e..ee11028481e4b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -272,12 +272,12 @@ def serialize(self) -> JsonDict: class AnyType(Type): """The type 'Any'.""" - # Does this come from a silent import? Used for --disallow-implicit-any-types flag - is_from_silent_import = False - - def __init__(self, implicit: bool = False, line: int = -1, column: int = -1) -> None: + def __init__(self, implicit: bool = False, from_silent_import: bool = False, + line: int = -1, column: int = -1) -> None: super().__init__(line, column) - self.implicit = implicit + self.implicit = implicit # Was this Any type was inferred without a type annotation? + # Does this come from an unresolved import? See--disallow-implicit-any-types flag + self.from_silent_import = from_silent_import def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_any(self) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 52a395297c41d..e893529296493 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -393,6 +393,7 @@ def f(x: MyType) -> None: [out] main:2: error: Cannot find module named 'missing' main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) +main:4: error: Argument 1 to "f" is implicitly converted to "Any" due to import from unanalyzed module [case testDisallowImplicitAnyVariableDefinition] # flags: --ignore-missing-imports --disallow-implicit-any-types From 7abf396bfd8122f1c889746740e1ee0c2459b0ef Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 25 May 2017 12:20:35 -0700 Subject: [PATCH 06/14] revert typeshed commit --- typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typeshed b/typeshed index 85d017d1e8ac4..890190d3493e7 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit 85d017d1e8ac4b86ece84a5ea44720d295d1dd61 +Subproject commit 890190d3493e7f9cafbe8049a9f7a4dfcd62f94d From 16f8d757984173227a402f980a361adf3f9671fc Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 25 May 2017 12:23:21 -0700 Subject: [PATCH 07/14] revert typeshed commit --- typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typeshed b/typeshed index 890190d3493e7..c2e6a6f670f5d 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit 890190d3493e7f9cafbe8049a9f7a4dfcd62f94d +Subproject commit c2e6a6f670f5d75c07eb31cf2509db302825b76d From e3b04f4e27192df96741a23ff1e9055f2fe3a05e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 25 May 2017 13:30:14 -0700 Subject: [PATCH 08/14] Inline silent_mode method --- mypy/build.py | 4 +++- mypy/options.py | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 16a4451202a28..efc02bcb438ea 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1387,7 +1387,9 @@ def fix_suppressed_dependencies(self, graph: Graph) -> None: """ # TODO: See if it's possible to move this check directly into parse_file in some way. # TODO: Find a way to write a test case for this fix. - if not self.options.silent_mode(): + silent_mode = (self.options.disallow_implicit_any_types or + self.options.follow_imports == 'skip') + if not silent_mode: return new_suppressed = [] diff --git a/mypy/options.py b/mypy/options.py index 697df0cf82a53..a0ed5047a8f70 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -160,6 +160,3 @@ def module_matches_pattern(self, module: str, pattern: Pattern[str]) -> bool: def select_options_affecting_cache(self) -> Mapping[str, bool]: return {opt: getattr(self, opt) for opt in self.OPTIONS_AFFECTING_CACHE} - - def silent_mode(self) -> bool: - return self.ignore_missing_imports or self.follow_imports == 'skip' From 4a2eca5d0e1f313b74baa4a8e1c92668e38c79f7 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 25 May 2017 13:31:19 -0700 Subject: [PATCH 09/14] Fix typo (was using wrong option) --- mypy/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index efc02bcb438ea..f803929a8fdfd 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1387,7 +1387,7 @@ def fix_suppressed_dependencies(self, graph: Graph) -> None: """ # TODO: See if it's possible to move this check directly into parse_file in some way. # TODO: Find a way to write a test case for this fix. - silent_mode = (self.options.disallow_implicit_any_types or + silent_mode = (self.options.ignore_missing_imports or self.options.follow_imports == 'skip') if not silent_mode: return From 7aea3468a53c5aa11b7a716478d2a224faa59173 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 25 May 2017 15:22:48 -0700 Subject: [PATCH 10/14] Only report an error if this is a first assignment Also, handle multiple declaration --- mypy/checker.py | 12 ++++++++---- mypy/messages.py | 2 ++ test-data/unit/check-flags.test | 11 +++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index ea45a2810627b..64fae2c2ad943 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1190,6 +1190,14 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: """ self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax) + if (s.type is not None and + self.options.disallow_implicit_any_types and + has_any_from_silent_import(s.type)): + if isinstance(s.lvalues[-1], TupleExpr): # is multiple + self.msg.fail(messages.IMPLICIT_CONVERT_TO_ANY_SILENT_IMPORT, s) + else: + self.msg.implicit_any_from_silent_import("Type of variable", s.type, s) + if len(s.lvalues) > 1: # Chained assignment (e.g. x = y = ...). # Make sure that rvalue type will not be reinferred. @@ -1717,10 +1725,6 @@ def check_simple_assignment(self, lvalue_type: Type, rvalue: Expression, self.msg.deleted_as_rvalue(rvalue_type, context) if isinstance(lvalue_type, DeletedType): self.msg.deleted_as_lvalue(lvalue_type, context) - elif (self.options.disallow_implicit_any_types - and has_any_from_silent_import(lvalue_type)): - prefix = "Type of {}".format(lvalue_name) - self.msg.implicit_any_from_silent_import(prefix, lvalue_type, context) else: self.check_subtype(rvalue_type, lvalue_type, context, msg, '{} has type'.format(rvalue_name), diff --git a/mypy/messages.py b/mypy/messages.py index ddd92bb360f81..32d34d5aa7d65 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -88,6 +88,8 @@ NON_BOOLEAN_IN_CONDITIONAL = 'Condition must be a boolean' DUPLICATE_TYPE_SIGNATURES = 'Function has duplicate type signatures' GENERIC_INSTANCE_VAR_CLASS_ACCESS = 'Access to generic instance variables via class is ambiguous' +IMPLICIT_CONVERT_TO_ANY_SILENT_IMPORT = 'A type on this line is implicitly converted to "Any" ' \ + 'due to import from unanalyzed module' ARG_CONSTRUCTOR_NAMES = { ARG_POS: "Arg", diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index e893529296493..8cc13cff610a0 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -495,3 +495,14 @@ from typing import Any class C(Any): # E: Class cannot subclass 'Any' (has type 'Any') pass + +[case testDisallowImplicitAnyVarDeclaration] +# flags: --ignore-missing-imports --disallow-implicit-any-types +from missing import Unchecked + +foo: Unchecked = "" +foo = "" +x, y = 1, 2 # type: Unchecked, Unchecked +[out] +main:4: error: Type of variable is implicitly converted to "Any" due to import from unanalyzed module +main:6: error: A type on this line is implicitly converted to "Any" due to import from unanalyzed module From 952c63b328044a0223b9aeadbc23cb506f86e9f4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Fri, 26 May 2017 16:48:11 -0700 Subject: [PATCH 11/14] Address code review feedback from David --- mypy/checker.py | 4 +++- mypy/types.py | 12 ++++++++---- test-data/unit/check-flags.test | 3 +++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 64fae2c2ad943..7707e37e18729 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1193,7 +1193,9 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if (s.type is not None and self.options.disallow_implicit_any_types and has_any_from_silent_import(s.type)): - if isinstance(s.lvalues[-1], TupleExpr): # is multiple + if isinstance(s.lvalues[-1], TupleExpr): + # This is a multiple assignment. Instead of figuring out which type is problematic, + # give a generic error message. self.msg.fail(messages.IMPLICIT_CONVERT_TO_ANY_SILENT_IMPORT, s) else: self.msg.implicit_any_from_silent_import("Type of variable", s.type, s) diff --git a/mypy/types.py b/mypy/types.py index ee11028481e4b..d04624489a9e2 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -272,11 +272,15 @@ def serialize(self) -> JsonDict: class AnyType(Type): """The type 'Any'.""" - def __init__(self, implicit: bool = False, from_silent_import: bool = False, - line: int = -1, column: int = -1) -> None: + def __init__(self, + implicit: bool = False, + from_silent_import: bool = False, + line: int = -1, + column: int = -1) -> None: super().__init__(line, column) - self.implicit = implicit # Was this Any type was inferred without a type annotation? - # Does this come from an unresolved import? See--disallow-implicit-any-types flag + # Was this Any type was inferred without a type annotation? + self.implicit = implicit + # Does this come from an unresolved import? See --disallow-implicit-any-types flag self.from_silent_import = from_silent_import def accept(self, visitor: 'TypeVisitor[T]') -> T: diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 8cc13cff610a0..9a90e760921b7 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -437,6 +437,7 @@ X = List[Unchecked] def f(x: X) -> None: # E: Argument 1 to "f" is implicitly converted to List[Any] due to import from unanalyzed module pass [builtins fixtures/list.pyi] + [case testDisallowImplicitAnyCast] # flags: --ignore-missing-imports --disallow-implicit-any-types from missing import Unchecked @@ -478,6 +479,7 @@ from missing import Unchecked Baz = NewType('Baz', Unchecked) [out] main:5: error: Argument 2 to NewType(...) must be subclassable (got Any) + [case testDisallowImplicitAnyCallableAndTuple] # flags: --ignore-missing-imports --disallow-implicit-any-types from typing import Callable, Tuple @@ -489,6 +491,7 @@ def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]: [out] main:5: error: Return type is implicitly converted to "Tuple[Any]" due to import from unanalyzed module main:5: error: Argument 1 to "foo" is implicitly converted to Callable[[], Any] due to import from unanalyzed module + [case testDisallowImplicitAnySubclassingExplicitAny] # flags: --ignore-missing-imports --disallow-implicit-any-types --disallow-subclassing-any from typing import Any From b764a5020cb64b11cf2056ab4bc1d5b2ff45e003 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Mon, 5 Jun 2017 18:53:54 -0700 Subject: [PATCH 12/14] Rename option to `--disallow-any=unimported`, modify error messages and wording in variable names and comments to reflect the name change. --- mypy/checker.py | 22 ++++++------ mypy/checkexpr.py | 12 +++---- mypy/main.py | 18 ++++++++-- mypy/messages.py | 9 +++-- mypy/options.py | 6 ++-- mypy/semanal.py | 23 ++++++------ mypy/typeanal.py | 12 +++---- mypy/types.py | 6 ++-- test-data/unit/check-flags.test | 62 ++++++++++++++++----------------- 9 files changed, 89 insertions(+), 81 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7707e37e18729..4aa5d81d1086a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -29,7 +29,7 @@ ARG_POS, MDEF, CONTRAVARIANT, COVARIANT) from mypy import nodes -from mypy.typeanal import has_any_from_silent_import +from mypy.typeanal import has_any_from_unimported_type from mypy.types import ( Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneTyp, strip_type, TypeType, @@ -612,17 +612,15 @@ def is_implicit_any(t: Type) -> bool: self.fail(messages.RETURN_TYPE_EXPECTED, fdef) if any(is_implicit_any(t) for t in fdef.type.arg_types): self.fail(messages.ARGUMENT_TYPE_EXPECTED, fdef) - if self.options.disallow_implicit_any_types: + if 'unimported' in self.options.disallow_any: if fdef.type and isinstance(fdef.type, CallableType): ret_type = fdef.type.ret_type - if has_any_from_silent_import(ret_type): - self.msg.implicit_any_from_silent_import("Return type", ret_type, - fdef) + if has_any_from_unimported_type(ret_type): + self.msg.unimported_type_becomes_any("Return type", ret_type, fdef) for idx, arg_type in enumerate(fdef.type.arg_types): - if has_any_from_silent_import(arg_type): + if has_any_from_unimported_type(arg_type): prefix = "Argument {} to \"{}\"".format(idx + 1, fdef.name()) - self.msg.implicit_any_from_silent_import(prefix, arg_type, - fdef) + self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) if name in nodes.reverse_op_method_set: self.check_reverse_op_method(item, typ, name) elif name in ('__getattr__', '__getattribute__'): @@ -1191,14 +1189,14 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax) if (s.type is not None and - self.options.disallow_implicit_any_types and - has_any_from_silent_import(s.type)): + 'unimported' in self.options.disallow_any and + has_any_from_unimported_type(s.type)): if isinstance(s.lvalues[-1], TupleExpr): # This is a multiple assignment. Instead of figuring out which type is problematic, # give a generic error message. - self.msg.fail(messages.IMPLICIT_CONVERT_TO_ANY_SILENT_IMPORT, s) + self.msg.unimported_type_becomes_any("A type on this line", AnyType(), s) else: - self.msg.implicit_any_from_silent_import("Type of variable", s.type, s) + self.msg.unimported_type_becomes_any("Type of variable", s.type, s) if len(s.lvalues) > 1: # Chained assignment (e.g. x = y = ...). diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 7ac5990fb3bbf..4add26e680817 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4,7 +4,7 @@ from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional from mypy.errors import report_internal_error -from mypy.typeanal import has_any_from_silent_import +from mypy.typeanal import has_any_from_unimported_type from mypy.types import ( Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef, TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType, @@ -1547,8 +1547,8 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: options = self.chk.options if options.warn_redundant_casts and is_same_type(source_type, target_type): self.msg.redundant_cast(target_type, expr) - if options.disallow_implicit_any_types and has_any_from_silent_import(target_type): - self.msg.implicit_any_from_silent_import("Target type of cast", target_type, expr) + if 'unimported' in options.disallow_any and has_any_from_unimported_type(target_type): + self.msg.unimported_type_becomes_any("Target type of cast", target_type, expr) return target_type def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type: @@ -2235,9 +2235,9 @@ def visit_newtype_expr(self, e: NewTypeExpr) -> Type: def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type: tuple_type = e.info.tuple_type if tuple_type: - if (self.chk.options.disallow_implicit_any_types and - has_any_from_silent_import(tuple_type)): - self.msg.implicit_any_from_silent_import("NamedTuple type", tuple_type, e) + if ('unimported' in self.chk.options.disallow_any and + has_any_from_unimported_type(tuple_type)): + self.msg.unimported_type_becomes_any("NamedTuple type", tuple_type, e) # TODO: Perhaps return a type object type? return AnyType() diff --git a/mypy/main.py b/mypy/main.py index cc0ad6bb0cf55..c82758ccb9e32 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -179,6 +179,7 @@ def process_options(args: List[str], strict_flag_names = [] # type: List[str] strict_flag_assignments = [] # type: List[Tuple[str, bool]] + valid_disallow_any_options = {'unimported'} def add_invertible_flag(flag: str, *, @@ -203,6 +204,16 @@ def add_invertible_flag(flag: str, strict_flag_names.append(flag) strict_flag_assignments.append((dest, not default)) + def disallow_any_argument_type(raw_options: str) -> List[str]: + current_options = raw_options.split(',') + for option in current_options: + if option not in valid_disallow_any_options: + formatted_opts = ', '.join(map("'{}'".format, valid_disallow_any_options)) + message = "Unrecognized option '{}' (valid options are: {}).".format( + option, formatted_opts) + raise argparse.ArgumentError(None, message) + return current_options + # Unless otherwise specified, arguments will be parsed directly onto an # Options object. Options that require further processing should have # their `dest` prefixed with `special-opts:`, which will cause them to be @@ -222,9 +233,10 @@ def add_invertible_flag(flag: str, help="silently ignore imports of missing modules") parser.add_argument('--follow-imports', choices=['normal', 'silent', 'skip', 'error'], default='normal', help="how to treat imports (default normal)") - parser.add_argument('--disallow-implicit-any-types', action='store_true', - help="disallow implicit conversion of types from unanalyzed modules" - " into Any") + parser.add_argument('--disallow-any', type=disallow_any_argument_type, default=[], + metavar='{{{}}}'.format(', '.join(valid_disallow_any_options)), + help="disallow various types of Any in a module. Takes a comma-separated " + "list of options (defaults to all options disabled)") add_invertible_flag('--disallow-untyped-calls', default=False, strict_flag=True, help="disallow calling functions without type annotations" " from functions with type annotations") diff --git a/mypy/messages.py b/mypy/messages.py index 32d34d5aa7d65..ef7c791cbae92 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -88,8 +88,6 @@ NON_BOOLEAN_IN_CONDITIONAL = 'Condition must be a boolean' DUPLICATE_TYPE_SIGNATURES = 'Function has duplicate type signatures' GENERIC_INSTANCE_VAR_CLASS_ACCESS = 'Access to generic instance variables via class is ambiguous' -IMPLICIT_CONVERT_TO_ANY_SILENT_IMPORT = 'A type on this line is implicitly converted to "Any" ' \ - 'due to import from unanalyzed module' ARG_CONSTRUCTOR_NAMES = { ARG_POS: "Arg", @@ -857,9 +855,10 @@ def unsupported_type_type(self, item: Type, context: Context) -> None: def redundant_cast(self, typ: Type, context: Context) -> None: self.note('Redundant cast to {}'.format(self.format(typ)), context) - def implicit_any_from_silent_import(self, prefix: str, typ: Type, ctx: Context) -> None: - self.fail("{} is implicitly converted to {} due to import from unanalyzed module".format( - prefix, self.format(typ)), ctx) + def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: + self.fail("{} becomes {} due to an unfollowed import (such imports occur either " + "when the imported module does not exist or when --follow-imports=skip " + "is set)".format(prefix, self.format(typ)), ctx) def typeddict_instantiated_with_unexpected_items(self, expected_item_names: List[str], diff --git a/mypy/options.py b/mypy/options.py index a0ed5047a8f70..b558470520f35 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -2,7 +2,7 @@ import pprint import sys -from typing import Any, Mapping, Optional, Tuple, List, Pattern, Dict +from typing import Mapping, Optional, Tuple, List, Pattern, Dict from mypy import defaults @@ -19,7 +19,7 @@ class Options: PER_MODULE_OPTIONS = { "ignore_missing_imports", "follow_imports", - "disallow_implicit_any_types", + "disallow_any", "disallow_untyped_calls", "disallow_untyped_defs", "check_untyped_defs", @@ -45,7 +45,7 @@ def __init__(self) -> None: self.report_dirs = {} # type: Dict[str, str] self.ignore_missing_imports = False self.follow_imports = 'normal' # normal|silent|skip|error - self.disallow_implicit_any_types = False + self.disallow_any = [] # type: List[str] # Disallow calling untyped functions from typed ones self.disallow_untyped_calls = False diff --git a/mypy/semanal.py b/mypy/semanal.py index c4584c7eeb508..8272efeb61520 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -85,7 +85,7 @@ from mypy.nodes import implicit_module_attrs from mypy.typeanal import ( TypeAnalyser, TypeAnalyserPass3, analyze_type_alias, no_subscript_builtin_alias, - TypeVariableQuery, TypeVarList, remove_dups, has_any_from_silent_import + TypeVariableQuery, TypeVarList, remove_dups, has_any_from_unimported_type ) from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type @@ -975,13 +975,12 @@ def analyze_base_classes(self, defn: ClassDef) -> None: else: self.fail('Invalid base class', base_expr) info.fallback_to_any = True - if (self.options.disallow_implicit_any_types and - has_any_from_silent_import(base)): + if 'unimported' in self.options.disallow_any and has_any_from_unimported_type(base): if isinstance(base_expr, (NameExpr, MemberExpr)): prefix = "Base type {}".format(base_expr.name) else: prefix = "Base type" - self.msg.implicit_any_from_silent_import(prefix, base, base_expr) + self.msg.unimported_type_becomes_any(prefix, base, base_expr) # Add 'object' as implicit base if there is no other base class. if (not base_types and defn.fullname != 'builtins.object'): @@ -1436,7 +1435,7 @@ def add_unknown_symbol(self, name: str, context: Context, is_import: bool = Fals else: var._fullname = self.qualified_name(name) var.is_ready = True - var.type = AnyType(from_silent_import=is_import) + var.type = AnyType(from_unimported_type=is_import) var.is_suppressed_import = is_import self.add_symbol(name, SymbolTableNode(GDEF, var, self.cur_mod_id), context) @@ -1883,15 +1882,15 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None: return variance, upper_bound = res - if self.options.disallow_implicit_any_types: - for idx, constraint in enumerate(values): - if has_any_from_silent_import(constraint): - prefix = "Constraint {}".format(idx + 1) - self.msg.implicit_any_from_silent_import(prefix, constraint, s) + if 'unimported' in self.options.disallow_any: + for idx, constraint in enumerate(values, start=1): + if has_any_from_unimported_type(constraint): + prefix = "Constraint {}".format(idx) + self.msg.unimported_type_becomes_any(prefix, constraint, s) - if has_any_from_silent_import(upper_bound): + if has_any_from_unimported_type(upper_bound): prefix = "Upper bound of type variable" - self.msg.implicit_any_from_silent_import(prefix, upper_bound, s) + self.msg.unimported_type_becomes_any(prefix, upper_bound, s) # Yes, it's a valid type variable definition! Add it to the symbol table. node = self.lookup(name, s) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 0a446e4c86487..f9ff0ab66fe3d 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -232,7 +232,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type: # context. This is slightly problematic as it allows using the type 'Any' # as a base class -- however, this will fail soon at runtime so the problem # is pretty minor. - return AnyType(from_silent_import=True) + return AnyType(from_unimported_type=True) # Allow unbound type variables when defining an alias if not (self.aliasing and sym.kind == TVAR and self.tvar_scope.get_binding(sym) is None): @@ -731,21 +731,21 @@ def visit_callable_type(self, t: CallableType) -> TypeVarList: return [] -def has_any_from_silent_import(t: Type) -> bool: - """Return true if this type was converted to Any because of a silenced import. +def has_any_from_unimported_type(t: Type) -> bool: + """Return true if this type is Any because an import was not followed. If type t is such Any type or has type arguments that contain such Any type this function will return true. """ - return t.accept(HasAnyFromSilentImportQuery()) + return t.accept(HasAnyFromUnimportedType()) -class HasAnyFromSilentImportQuery(TypeQuery[bool]): +class HasAnyFromUnimportedType(TypeQuery[bool]): def __init__(self) -> None: super().__init__(any) def visit_any(self, t: AnyType) -> bool: - return t.from_silent_import + return t.from_unimported_type def make_optional_type(t: Type) -> Type: diff --git a/mypy/types.py b/mypy/types.py index d04624489a9e2..0cf44d724bb7d 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -274,14 +274,14 @@ class AnyType(Type): def __init__(self, implicit: bool = False, - from_silent_import: bool = False, + from_unimported_type: bool = False, line: int = -1, column: int = -1) -> None: super().__init__(line, column) # Was this Any type was inferred without a type annotation? self.implicit = implicit - # Does this come from an unresolved import? See --disallow-implicit-any-types flag - self.from_silent_import = from_silent_import + # Does this come from an unfollowed import? See --disallow-any=unimported option + self.from_unimported_type = from_unimported_type def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_any(self) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 9a90e760921b7..87bae32383b3a 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -378,14 +378,14 @@ if z: # E: Condition must be a boolean [builtins fixtures/bool.pyi] [case testDisallowImplicitTypesIgnoreMissingTypes] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import MyType -def f(x: MyType) -> None: # E: Argument 1 to "f" is implicitly converted to "Any" due to import from unanalyzed module +def f(x: MyType) -> None: # E: Argument 1 to "f" becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) pass [case testDisallowImplicitTypes] -# flags: --disallow-implicit-any-types +# flags: --disallow-any=unimported from missing import MyType def f(x: MyType) -> None: @@ -393,16 +393,16 @@ def f(x: MyType) -> None: [out] main:2: error: Cannot find module named 'missing' main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) -main:4: error: Argument 1 to "f" is implicitly converted to "Any" due to import from unanalyzed module +main:4: error: Argument 1 to "f" becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [case testDisallowImplicitAnyVariableDefinition] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked -t: Unchecked = 12 # E: Type of variable is implicitly converted to "Any" due to import from unanalyzed module +t: Unchecked = 12 # E: Type of variable becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [case testDisallowImplicitAnyGeneric] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked from typing import List @@ -411,46 +411,46 @@ def foo(l: List[Unchecked]) -> List[Unchecked]: return l [builtins fixtures/list.pyi] [out] -main:5: error: Return type is implicitly converted to List[Any] due to import from unanalyzed module -main:5: error: Argument 1 to "foo" is implicitly converted to List[Any] due to import from unanalyzed module -main:6: error: Type of variable is implicitly converted to List[Any] due to import from unanalyzed module +main:5: error: Return type becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: Argument 1 to "foo" becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:6: error: Type of variable becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [case testDisallowImplicitAnyInherit] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked from typing import List -class C(Unchecked): # E: Base type Unchecked is implicitly converted to "Any" due to import from unanalyzed module +class C(Unchecked): # E: Base type Unchecked becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) pass -class A(List[Unchecked]): # E: Base type is implicitly converted to List[Any] due to import from unanalyzed module +class A(List[Unchecked]): # E: Base type becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) pass [builtins fixtures/list.pyi] [case testDisallowImplicitAnyAlias] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked from typing import List X = List[Unchecked] -def f(x: X) -> None: # E: Argument 1 to "f" is implicitly converted to List[Any] due to import from unanalyzed module +def f(x: X) -> None: # E: Argument 1 to "f" becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) pass [builtins fixtures/list.pyi] [case testDisallowImplicitAnyCast] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked from typing import List, cast foo = [1, 2, 3] -cast(List[Unchecked], foo) # E: Target type of cast is implicitly converted to List[Any] due to import from unanalyzed module -cast(Unchecked, foo) # E: Target type of cast is implicitly converted to "Any" due to import from unanalyzed module +cast(List[Unchecked], foo) # E: Target type of cast becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +cast(Unchecked, foo) # E: Target type of cast becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [builtins fixtures/list.pyi] [case testDisallowImplicitAnyNamedTuple] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from typing import List, NamedTuple from missing import Unchecked @@ -458,21 +458,21 @@ Point = NamedTuple('Point', [('x', List[Unchecked]), ('y', Unchecked)]) [builtins fixtures/list.pyi] [out] -main:5: error: NamedTuple type is implicitly converted to "Tuple[List[Any], Any]" due to import from unanalyzed module +main:5: error: NamedTuple type becomes "Tuple[List[Any], Any]" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [case testDisallowImplicitAnyTypeVarConstraints] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from typing import List, NamedTuple, TypeVar, Any from missing import Unchecked T = TypeVar('T', Unchecked, List[Unchecked], str) [builtins fixtures/list.pyi] [out] -main:5: error: Constraint 1 is implicitly converted to "Any" due to import from unanalyzed module -main:5: error: Constraint 2 is implicitly converted to List[Any] due to import from unanalyzed module +main:5: error: Constraint 1 becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: Constraint 2 becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [case testDisallowImplicitAnyNewType] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from typing import NewType from missing import Unchecked @@ -481,7 +481,7 @@ Baz = NewType('Baz', Unchecked) main:5: error: Argument 2 to NewType(...) must be subclassable (got Any) [case testDisallowImplicitAnyCallableAndTuple] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from typing import Callable, Tuple from missing import Unchecked @@ -489,23 +489,23 @@ def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]: return f() [builtins fixtures/list.pyi] [out] -main:5: error: Return type is implicitly converted to "Tuple[Any]" due to import from unanalyzed module -main:5: error: Argument 1 to "foo" is implicitly converted to Callable[[], Any] due to import from unanalyzed module +main:5: error: Return type becomes "Tuple[Any]" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: Argument 1 to "foo" becomes Callable[[], Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) [case testDisallowImplicitAnySubclassingExplicitAny] -# flags: --ignore-missing-imports --disallow-implicit-any-types --disallow-subclassing-any +# flags: --ignore-missing-imports --disallow-any=unimported --disallow-subclassing-any from typing import Any class C(Any): # E: Class cannot subclass 'Any' (has type 'Any') pass [case testDisallowImplicitAnyVarDeclaration] -# flags: --ignore-missing-imports --disallow-implicit-any-types +# flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked foo: Unchecked = "" foo = "" x, y = 1, 2 # type: Unchecked, Unchecked [out] -main:4: error: Type of variable is implicitly converted to "Any" due to import from unanalyzed module -main:6: error: A type on this line is implicitly converted to "Any" due to import from unanalyzed module +main:4: error: Type of variable becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:6: error: A type on this line becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) From 845a6714000b6f9ef2f6bd8db2d9f43cab281fea Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Tue, 6 Jun 2017 18:17:46 -0700 Subject: [PATCH 13/14] Address code review feedback --- mypy/main.py | 19 ++++++++--------- mypy/messages.py | 5 ++--- test-data/unit/check-flags.test | 36 ++++++++++++++++----------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index c82758ccb9e32..485a61ef0067d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -179,7 +179,7 @@ def process_options(args: List[str], strict_flag_names = [] # type: List[str] strict_flag_assignments = [] # type: List[Tuple[str, bool]] - valid_disallow_any_options = {'unimported'} + disallow_any_options = ['unimported'] def add_invertible_flag(flag: str, *, @@ -205,14 +205,15 @@ def add_invertible_flag(flag: str, strict_flag_assignments.append((dest, not default)) def disallow_any_argument_type(raw_options: str) -> List[str]: - current_options = raw_options.split(',') - for option in current_options: - if option not in valid_disallow_any_options: - formatted_opts = ', '.join(map("'{}'".format, valid_disallow_any_options)) - message = "Unrecognized option '{}' (valid options are: {}).".format( - option, formatted_opts) + options = raw_options.split(',') + for option in options: + if option not in disallow_any_options: + formatted_valid_options = ', '.join( + "'{}'".format(option) for option in disallow_any_options) + message = "Invalid '--disallow-any' option '{}' (valid options are: {}).".format( + option, formatted_valid_options) raise argparse.ArgumentError(None, message) - return current_options + return options # Unless otherwise specified, arguments will be parsed directly onto an # Options object. Options that require further processing should have @@ -234,7 +235,7 @@ def disallow_any_argument_type(raw_options: str) -> List[str]: parser.add_argument('--follow-imports', choices=['normal', 'silent', 'skip', 'error'], default='normal', help="how to treat imports (default normal)") parser.add_argument('--disallow-any', type=disallow_any_argument_type, default=[], - metavar='{{{}}}'.format(', '.join(valid_disallow_any_options)), + metavar='{{{}}}'.format(', '.join(disallow_any_options)), help="disallow various types of Any in a module. Takes a comma-separated " "list of options (defaults to all options disabled)") add_invertible_flag('--disallow-untyped-calls', default=False, strict_flag=True, diff --git a/mypy/messages.py b/mypy/messages.py index ef7c791cbae92..c63b44905d51c 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -856,9 +856,8 @@ def redundant_cast(self, typ: Type, context: Context) -> None: self.note('Redundant cast to {}'.format(self.format(typ)), context) def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: - self.fail("{} becomes {} due to an unfollowed import (such imports occur either " - "when the imported module does not exist or when --follow-imports=skip " - "is set)".format(prefix, self.format(typ)), ctx) + self.fail("{} becomes {} due to an unfollowed import".format(prefix, self.format(typ)), + ctx) def typeddict_instantiated_with_unexpected_items(self, expected_item_names: List[str], diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 87bae32383b3a..6f8dcdd4a94b3 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -381,7 +381,7 @@ if z: # E: Condition must be a boolean # flags: --ignore-missing-imports --disallow-any=unimported from missing import MyType -def f(x: MyType) -> None: # E: Argument 1 to "f" becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +def f(x: MyType) -> None: # E: Argument 1 to "f" becomes "Any" due to an unfollowed import pass [case testDisallowImplicitTypes] @@ -393,13 +393,13 @@ def f(x: MyType) -> None: [out] main:2: error: Cannot find module named 'missing' main:2: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) -main:4: error: Argument 1 to "f" becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:4: error: Argument 1 to "f" becomes "Any" due to an unfollowed import [case testDisallowImplicitAnyVariableDefinition] # flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked -t: Unchecked = 12 # E: Type of variable becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +t: Unchecked = 12 # E: Type of variable becomes "Any" due to an unfollowed import [case testDisallowImplicitAnyGeneric] # flags: --ignore-missing-imports --disallow-any=unimported @@ -411,19 +411,19 @@ def foo(l: List[Unchecked]) -> List[Unchecked]: return l [builtins fixtures/list.pyi] [out] -main:5: error: Return type becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) -main:5: error: Argument 1 to "foo" becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) -main:6: error: Type of variable becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: Return type becomes List[Any] due to an unfollowed import +main:5: error: Argument 1 to "foo" becomes List[Any] due to an unfollowed import +main:6: error: Type of variable becomes List[Any] due to an unfollowed import [case testDisallowImplicitAnyInherit] # flags: --ignore-missing-imports --disallow-any=unimported from missing import Unchecked from typing import List -class C(Unchecked): # E: Base type Unchecked becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +class C(Unchecked): # E: Base type Unchecked becomes "Any" due to an unfollowed import pass -class A(List[Unchecked]): # E: Base type becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +class A(List[Unchecked]): # E: Base type becomes List[Any] due to an unfollowed import pass [builtins fixtures/list.pyi] @@ -434,7 +434,7 @@ from typing import List X = List[Unchecked] -def f(x: X) -> None: # E: Argument 1 to "f" becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +def f(x: X) -> None: # E: Argument 1 to "f" becomes List[Any] due to an unfollowed import pass [builtins fixtures/list.pyi] @@ -445,8 +445,8 @@ from typing import List, cast foo = [1, 2, 3] -cast(List[Unchecked], foo) # E: Target type of cast becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) -cast(Unchecked, foo) # E: Target type of cast becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +cast(List[Unchecked], foo) # E: Target type of cast becomes List[Any] due to an unfollowed import +cast(Unchecked, foo) # E: Target type of cast becomes "Any" due to an unfollowed import [builtins fixtures/list.pyi] [case testDisallowImplicitAnyNamedTuple] @@ -458,7 +458,7 @@ Point = NamedTuple('Point', [('x', List[Unchecked]), ('y', Unchecked)]) [builtins fixtures/list.pyi] [out] -main:5: error: NamedTuple type becomes "Tuple[List[Any], Any]" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: NamedTuple type becomes "Tuple[List[Any], Any]" due to an unfollowed import [case testDisallowImplicitAnyTypeVarConstraints] # flags: --ignore-missing-imports --disallow-any=unimported @@ -468,8 +468,8 @@ from missing import Unchecked T = TypeVar('T', Unchecked, List[Unchecked], str) [builtins fixtures/list.pyi] [out] -main:5: error: Constraint 1 becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) -main:5: error: Constraint 2 becomes List[Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: Constraint 1 becomes "Any" due to an unfollowed import +main:5: error: Constraint 2 becomes List[Any] due to an unfollowed import [case testDisallowImplicitAnyNewType] # flags: --ignore-missing-imports --disallow-any=unimported @@ -489,8 +489,8 @@ def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]: return f() [builtins fixtures/list.pyi] [out] -main:5: error: Return type becomes "Tuple[Any]" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) -main:5: error: Argument 1 to "foo" becomes Callable[[], Any] due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:5: error: Return type becomes "Tuple[Any]" due to an unfollowed import +main:5: error: Argument 1 to "foo" becomes Callable[[], Any] due to an unfollowed import [case testDisallowImplicitAnySubclassingExplicitAny] # flags: --ignore-missing-imports --disallow-any=unimported --disallow-subclassing-any @@ -507,5 +507,5 @@ foo: Unchecked = "" foo = "" x, y = 1, 2 # type: Unchecked, Unchecked [out] -main:4: error: Type of variable becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) -main:6: error: A type on this line becomes "Any" due to an unfollowed import (such imports occur either when the imported module does not exist or when --follow-imports=skip is set) +main:4: error: Type of variable becomes "Any" due to an unfollowed import +main:6: error: A type on this line becomes "Any" due to an unfollowed import From feeba7b1aec00c7bdf8995c4698c54e47bd19738 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Wed, 7 Jun 2017 11:35:19 -0700 Subject: [PATCH 14/14] Rename variables that were shadowing other variables from outer scope --- mypy/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 485a61ef0067d..5229071773baa 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -205,15 +205,15 @@ def add_invertible_flag(flag: str, strict_flag_assignments.append((dest, not default)) def disallow_any_argument_type(raw_options: str) -> List[str]: - options = raw_options.split(',') - for option in options: + flag_options = raw_options.split(',') + for option in flag_options: if option not in disallow_any_options: formatted_valid_options = ', '.join( - "'{}'".format(option) for option in disallow_any_options) + "'{}'".format(o) for o in disallow_any_options) message = "Invalid '--disallow-any' option '{}' (valid options are: {}).".format( option, formatted_valid_options) raise argparse.ArgumentError(None, message) - return options + return flag_options # Unless otherwise specified, arguments will be parsed directly onto an # Options object. Options that require further processing should have