From 959deaa6ee5cef945abeab0bab360baebb6f90b3 Mon Sep 17 00:00:00 2001 From: Max Murin Date: Mon, 30 Jan 2023 11:10:05 -0800 Subject: [PATCH 1/4] Add option to set types that mimic builtins as lowercase, pipe it through, modify tests so they don't all need to be changed --- mypy/build.py | 12 +- mypy/checker.py | 57 ++++-- mypy/checkexpr.py | 21 +- mypy/checkmember.py | 146 +++++++++----- mypy/checkpattern.py | 26 ++- mypy/checkstrformat.py | 2 +- mypy/errors.py | 34 ++-- mypy/fastparse.py | 2 +- mypy/inspections.py | 4 +- mypy/main.py | 7 + mypy/messages.py | 269 ++++++++++++++++---------- mypy/nodes.py | 27 +-- mypy/options.py | 7 + mypy/plugins/attrs.py | 2 +- mypy/plugins/ctypes.py | 14 +- mypy/plugins/singledispatch.py | 15 +- mypy/semanal.py | 4 +- mypy/semanal_newtype.py | 6 +- mypy/semanal_typeargs.py | 10 +- mypy/strconv.py | 32 ++- mypy/stubgen.py | 4 +- mypy/stubtest.py | 2 +- mypy/suggestions.py | 7 +- mypy/test/helpers.py | 1 + mypy/test/testcheck.py | 2 + mypy/test/testcmdline.py | 2 + mypy/test/testgraph.py | 2 +- mypy/test/testmerge.py | 16 +- mypy/test/testparse.py | 3 +- mypy/test/testpythoneval.py | 1 + mypy/test/testsemanal.py | 3 +- mypy/test/testtransform.py | 3 +- mypy/test/testtypegen.py | 8 +- mypy/test/testtypes.py | 15 +- mypy/typeanal.py | 49 ++--- mypy/types.py | 16 +- mypyc/build.py | 2 +- mypyc/errors.py | 5 +- mypyc/irbuild/format_str_tokenizer.py | 5 +- mypyc/test/test_run.py | 2 +- mypyc/test/testutil.py | 2 +- test-data/unit/check-lowercase.test | 44 +++++ test-data/unit/check-varargs.test | 4 +- test-data/unit/semanal-errors.test | 2 +- 44 files changed, 599 insertions(+), 298 deletions(-) create mode 100644 test-data/unit/check-lowercase.test diff --git a/mypy/build.py b/mypy/build.py index 4713139236d03..b8e70b19b8cbd 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -235,17 +235,7 @@ def _build( source_set = BuildSourceSet(sources) cached_read = fscache.read - errors = Errors( - options.show_error_context, - options.show_column_numbers, - options.hide_error_codes, - options.pretty, - options.show_error_end, - lambda path: read_py_file(path, cached_read), - options.show_absolute_path, - options.many_errors_threshold, - options, - ) + errors = Errors(options, read_source=lambda path: read_py_file(path, cached_read)) plugin, snapshot = load_plugins(options, errors, stdout, extra_plugins) # Add catch-all .gitignore to cache dir if we created it diff --git a/mypy/checker.py b/mypy/checker.py index 2b865f645330e..29ff7d4b7c7a8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -419,7 +419,7 @@ def __init__( self.expr_checker = mypy.checkexpr.ExpressionChecker( self, self.msg, self.plugin, per_line_checking_time_ns ) - self.pattern_checker = PatternChecker(self, self.msg, self.plugin) + self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options) @property def type_context(self) -> list[Type | None]: @@ -483,7 +483,9 @@ def check_first_pass(self) -> None: "typing.Sequence", [self.named_type("builtins.str")] ) if not is_subtype(all_.type, seq_str): - str_seq_s, all_s = format_type_distinctly(seq_str, all_.type) + str_seq_s, all_s = format_type_distinctly( + seq_str, all_.type, options=self.options + ) self.fail( message_registry.ALL_MUST_BE_SEQ_STR.format(str_seq_s, all_s), all_node ) @@ -686,6 +688,7 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: msg=self.msg, original_type=inner_type, chk=self, + options=self.options, ) ) if isinstance(inner_call, CallableType): @@ -1178,7 +1181,8 @@ def check_func_def( msg = None elif typ.arg_names[i] in {"self", "cls"}: msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format( - erased, ref_type + erased.str_with_options(self.options), + ref_type.str_with_options(self.options), ) else: msg = message_registry.MISSING_OR_INVALID_SELF_TYPE @@ -1323,7 +1327,7 @@ def check_unbound_return_typevar(self, typ: CallableType) -> None: ): self.note( "Consider using the upper bound " - f"{format_type(typ.ret_type.upper_bound)} instead", + f"{format_type(typ.ret_type.upper_bound, self.options)} instead", context=typ.ret_type, ) @@ -1430,7 +1434,9 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None: get_proper_type(bound_type.ret_type), (AnyType, Instance, TupleType, UninhabitedType) ): self.fail( - message_registry.NON_INSTANCE_NEW_TYPE.format(format_type(bound_type.ret_type)), + message_registry.NON_INSTANCE_NEW_TYPE.format( + format_type(bound_type.ret_type, self.options) + ), fdef, ) else: @@ -2347,7 +2353,10 @@ class Baz(int, Foo, Bar, enum.Flag): ... enum_base = base continue elif enum_base is not None and not base.type.is_enum: - self.fail(f'No non-enum mixin classes are allowed after "{enum_base}"', defn) + self.fail( + f'No non-enum mixin classes are allowed after "{enum_base.str_with_options(self.options)}"', + defn, + ) break def check_enum_new(self, defn: ClassDef) -> None: @@ -2372,7 +2381,7 @@ def has_new_method(info: TypeInfo) -> bool: if candidate and has_new: self.fail( "Only a single data type mixin is allowed for Enum subtypes, " - 'found extra "{}"'.format(base), + 'found extra "{}"'.format(base.str_with_options(self.options)), defn, ) elif candidate: @@ -3963,7 +3972,7 @@ def check_member_assignment( msg=self.msg, chk=self, ) - get_type = analyze_descriptor_access(attribute_type, mx) + get_type = analyze_descriptor_access(attribute_type, mx, self.options) if not attribute_type.type.has_readable_member("__set__"): # If there is no __set__, we type-check that the assigned value matches # the return type of __get__. This doesn't match the python semantics, @@ -3974,7 +3983,12 @@ def check_member_assignment( dunder_set = attribute_type.type.get_method("__set__") if dunder_set is None: - self.fail(message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(attribute_type), context) + self.fail( + message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format( + attribute_type.str_with_options(self.options) + ), + context, + ) return AnyType(TypeOfAny.from_error), get_type, False bound_method = analyze_decorator_or_funcbase_access( @@ -3984,6 +3998,7 @@ def check_member_assignment( self_type=attribute_type, name="__set__", mx=mx, + options=self.options, ) typ = map_instance_to_supertype(attribute_type, dunder_set.info) dunder_set_type = expand_type_by_instance(bound_method, typ) @@ -4128,7 +4143,9 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> None: if error_note_and_code: error_note, code = error_note_and_code self.fail( - message_registry.TYPE_MUST_BE_USED.format(format_type(expr_type)), s, code=code + message_registry.TYPE_MUST_BE_USED.format(format_type(expr_type, self.options)), + s, + code=code, ) self.note(error_note, s, code=code) @@ -4958,7 +4975,9 @@ def _make_fake_typeinfo_and_full_name( # We use the pretty_names_list for error messages but can't # use it for the real name that goes into the symbol table # because it can have dots in it. - pretty_names_list = pretty_seq(format_type_distinctly(*base_classes, bare=True), "and") + pretty_names_list = pretty_seq( + format_type_distinctly(*base_classes, options=self.options, bare=True), "and" + ) try: info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) with self.msg.filter_errors() as local_errors: @@ -4995,7 +5014,7 @@ def intersect_instance_callable(self, typ: Instance, callable_type: CallableType gen_name = gen_unique_name(f"", cur_module.names) # Synthesize a fake TypeInfo - short_name = format_type_bare(typ) + short_name = format_type_bare(typ, self.options) cdef, info = self.make_fake_typeinfo(cur_module.fullname, gen_name, short_name, [typ]) # Build up a fake FuncDef so we can populate the symbol table. @@ -5201,7 +5220,7 @@ def _check_for_truthy_type(self, t: Type, expr: Expression) -> None: return def format_expr_type() -> str: - typ = format_type(t) + typ = format_type(t, self.options) if isinstance(expr, MemberExpr): return f'Member "{expr.name}" has type {typ}' elif isinstance(expr, RefExpr) and expr.fullname: @@ -5216,14 +5235,16 @@ def format_expr_type() -> str: return f"Expression has type {typ}" if isinstance(t, FunctionLike): - self.fail(message_registry.FUNCTION_ALWAYS_TRUE.format(format_type(t)), expr) + self.fail( + message_registry.FUNCTION_ALWAYS_TRUE.format(format_type(t, self.options)), expr + ) elif isinstance(t, UnionType): self.fail(message_registry.TYPE_ALWAYS_TRUE_UNIONTYPE.format(format_expr_type()), expr) elif isinstance(t, Instance) and t.type.fullname == "typing.Iterable": _, info = self.make_fake_typeinfo("typing", "Collection", "Collection", []) self.fail( message_registry.ITERABLE_ALWAYS_TRUE.format( - format_expr_type(), format_type(Instance(info, t.args)) + format_expr_type(), format_type(Instance(info, t.args), self.options) ), expr, ) @@ -5692,6 +5713,7 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None: original_type=new_parent_type, chk=self, in_literal_context=False, + options=self.options, ) if w.has_new_errors(): return None @@ -6008,7 +6030,9 @@ def check_subtype( note_msg = "" notes = notes or [] if subtype_label is not None or supertype_label is not None: - subtype_str, supertype_str = format_type_distinctly(orig_subtype, orig_supertype) + subtype_str, supertype_str = format_type_distinctly( + orig_subtype, orig_supertype, options=self.options + ) if subtype_label is not None: extra_info.append(subtype_label + " " + subtype_str) if supertype_label is not None: @@ -6692,6 +6716,7 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: # This is not a real attribute lookup so don't mess with deferring nodes. no_deferral=True, module_symbol_table=module_symbol_table, + options=self.options, ) return not watcher.has_new_errors() diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c0c4e18d8f1f5..95fe8e58f83ae 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1244,6 +1244,7 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str chk=self.chk, in_literal_context=self.is_literal_context(), self_type=typ, + options=self.chk.options, ) narrowed = self.narrow_type_from_binder(e.callee, item, skip_non_overlapping=True) if narrowed is None: @@ -1319,6 +1320,7 @@ def check_call( original_type=callee, chk=self.chk, in_literal_context=self.is_literal_context(), + options=self.chk.options, ) callable_name = callee.type.fullname + ".__call__" # Apply method signature hook, if one exists @@ -2802,6 +2804,7 @@ def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type in_literal_context=self.is_literal_context(), module_symbol_table=module_symbol_table, is_self=is_self, + options=self.chk.options, ) return member_type @@ -2824,6 +2827,7 @@ def analyze_external_member_access( original_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), + options=self.chk.options, ) def is_literal_context(self) -> bool: @@ -3213,6 +3217,7 @@ def check_method_call_by_name( original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), + options=self.chk.options, ) return self.check_method_call(method, base_type, method_type, args, arg_kinds, context) @@ -3306,6 +3311,7 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None: msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), + options=self.chk.options, ) return None if w.has_new_errors() else member @@ -3967,7 +3973,12 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias): # Subscription of a (generic) alias in runtime context, expand the alias. item = expand_type_alias( - tapp.expr.node, tapp.types, self.chk.fail, tapp.expr.node.no_args, tapp + tapp.expr.node, + tapp.types, + self.chk.fail, + tapp.expr.node.no_args, + tapp, + self.chk.options, ) item = get_proper_type(item) if isinstance(item, Instance): @@ -4032,7 +4043,12 @@ class LongName(Generic[T]): ... disallow_any = self.chk.options.disallow_any_generics and self.is_callee item = get_proper_type( set_any_tvars( - alias, ctx.line, ctx.column, disallow_any=disallow_any, fail=self.msg.fail + alias, + ctx.line, + ctx.column, + self.chk.options, + disallow_any=disallow_any, + fail=self.msg.fail, ) ) if isinstance(item, Instance): @@ -4556,6 +4572,7 @@ def visit_super_expr(self, e: SuperExpr) -> Type: msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), + options=self.chk.options, ) assert False, "unreachable" diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2ba917715be05..6ffde770663ae 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -34,6 +34,7 @@ Var, is_final_node, ) +from mypy.options import Options from mypy.plugin import AttributeContext from mypy.typeops import ( bind_self, @@ -161,6 +162,7 @@ def analyze_member_access( module_symbol_table: SymbolTable | None = None, no_deferral: bool = False, is_self: bool = False, + options: Options, ) -> Type: """Return the type of attribute 'name' of 'typ'. @@ -198,7 +200,7 @@ def analyze_member_access( no_deferral=no_deferral, is_self=is_self, ) - result = _analyze_member_access(name, typ, mx, override_info) + result = _analyze_member_access(name, typ, mx, override_info, options=options) possible_literal = get_proper_type(result) if ( in_literal_context @@ -211,46 +213,58 @@ def analyze_member_access( def _analyze_member_access( - name: str, typ: Type, mx: MemberContext, override_info: TypeInfo | None = None + name: str, + typ: Type, + mx: MemberContext, + override_info: TypeInfo | None = None, + *, + options: Options, ) -> Type: # TODO: This and following functions share some logic with subtypes.find_member; # consider refactoring. typ = get_proper_type(typ) if isinstance(typ, Instance): - return analyze_instance_member_access(name, typ, mx, override_info) + return analyze_instance_member_access(name, typ, mx, override_info, options=options) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType(TypeOfAny.from_another_any, source_any=typ) elif isinstance(typ, UnionType): - return analyze_union_member_access(name, typ, mx) + return analyze_union_member_access(name, typ, mx, options=options) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): - return analyze_type_callable_member_access(name, typ, mx) + return analyze_type_callable_member_access(name, typ, mx, options=options) elif isinstance(typ, TypeType): - return analyze_type_type_member_access(name, typ, mx, override_info) + return analyze_type_type_member_access(name, typ, mx, override_info, options=options) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. - return _analyze_member_access(name, tuple_fallback(typ), mx, override_info) + return _analyze_member_access( + name, tuple_fallback(typ), mx, override_info, options=options + ) elif isinstance(typ, (LiteralType, FunctionLike)): # Actually look up from the fallback instance type. - return _analyze_member_access(name, typ.fallback, mx, override_info) + return _analyze_member_access(name, typ.fallback, mx, override_info, options=options) elif isinstance(typ, TypedDictType): - return analyze_typeddict_access(name, typ, mx, override_info) + return analyze_typeddict_access(name, typ, mx, override_info, options=options) elif isinstance(typ, NoneType): - return analyze_none_member_access(name, typ, mx) + return analyze_none_member_access(name, typ, mx, options=options) elif isinstance(typ, TypeVarLikeType): if isinstance(typ, TypeVarType) and typ.values: return _analyze_member_access( - name, make_simplified_union(typ.values), mx, override_info + name, make_simplified_union(typ.values), mx, override_info, options=options ) - return _analyze_member_access(name, typ.upper_bound, mx, override_info) + return _analyze_member_access(name, typ.upper_bound, mx, override_info, options=options) elif isinstance(typ, DeletedType): mx.msg.deleted_as_rvalue(typ, mx.context) return AnyType(TypeOfAny.from_error) - return report_missing_attribute(mx.original_type, typ, name, mx) + return report_missing_attribute(mx.original_type, typ, name, mx, options=options) def may_be_awaitable_attribute( - name: str, typ: Type, mx: MemberContext, override_info: TypeInfo | None = None + name: str, + typ: Type, + mx: MemberContext, + override_info: TypeInfo | None = None, + *, + options: Options, ) -> bool: """Check if the given type has the attribute when awaited.""" if mx.chk.checking_missing_await: @@ -260,7 +274,7 @@ def may_be_awaitable_attribute( aw_type = mx.chk.get_precise_awaitable_type(typ, local_errors) if aw_type is None: return False - _ = _analyze_member_access(name, aw_type, mx, override_info) + _ = _analyze_member_access(name, aw_type, mx, override_info, options=options) return not local_errors.has_new_errors() @@ -270,10 +284,12 @@ def report_missing_attribute( name: str, mx: MemberContext, override_info: TypeInfo | None = None, + *, + options: Options, ) -> Type: res_type = mx.msg.has_no_attr(original_type, typ, name, mx.context, mx.module_symbol_table) if not mx.msg.prefer_simple_messages(): - if may_be_awaitable_attribute(name, typ, mx, override_info): + if may_be_awaitable_attribute(name, typ, mx, override_info, options=options): mx.msg.possible_missing_await(mx.context) return res_type @@ -283,7 +299,12 @@ def report_missing_attribute( def analyze_instance_member_access( - name: str, typ: Instance, mx: MemberContext, override_info: TypeInfo | None + name: str, + typ: Instance, + mx: MemberContext, + override_info: TypeInfo | None, + *, + options: Options, ) -> Type: if name == "__init__" and not mx.is_super: # Accessing __init__ in statically typed code would compromise @@ -314,7 +335,7 @@ def analyze_instance_member_access( assert isinstance(method, OverloadedFuncDef) first_item = method.items[0] assert isinstance(first_item, Decorator) - return analyze_var(name, first_item.var, typ, info, mx) + return analyze_var(name, first_item.var, typ, info, mx, options=options) if mx.is_lvalue: mx.msg.cant_assign_to_method(mx.context) signature = function_type(method, mx.named_type("builtins.function")) @@ -340,7 +361,7 @@ def analyze_instance_member_access( return member_type else: # Not a method. - return analyze_member_var_access(name, typ, info, mx) + return analyze_member_var_access(name, typ, info, mx, options=options) def validate_super_call(node: FuncBase, mx: MemberContext) -> None: @@ -362,7 +383,9 @@ def validate_super_call(node: FuncBase, mx: MemberContext) -> None: mx.msg.unsafe_super(node.name, node.info.name, mx.context) -def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: MemberContext) -> Type: +def analyze_type_callable_member_access( + name: str, typ: FunctionLike, mx: MemberContext, *, options: Options +) -> Type: # Class attribute. # TODO super? ret_type = typ.items[0].ret_type @@ -388,18 +411,23 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member # See https://github.com/python/mypy/pull/1787 for more info. # TODO: do not rely on same type variables being present in all constructor overloads. result = analyze_class_attribute_access( - ret_type, name, mx, original_vars=typ.items[0].variables + ret_type, name, mx, original_vars=typ.items[0].variables, options=options ) if result: return result # Look up from the 'type' type. - return _analyze_member_access(name, typ.fallback, mx) + return _analyze_member_access(name, typ.fallback, mx, options=options) else: assert False, f"Unexpected type {ret_type!r}" def analyze_type_type_member_access( - name: str, typ: TypeType, mx: MemberContext, override_info: TypeInfo | None + name: str, + typ: TypeType, + mx: MemberContext, + override_info: TypeInfo | None, + *, + options: Options, ) -> Type: # Similar to analyze_type_callable_attribute_access. item = None @@ -408,7 +436,7 @@ def analyze_type_type_member_access( item = typ.item elif isinstance(typ.item, AnyType): with mx.msg.filter_errors(): - return _analyze_member_access(name, fallback, mx, override_info) + return _analyze_member_access(name, fallback, mx, override_info, options=options) elif isinstance(typ.item, TypeVarType): upper_bound = get_proper_type(typ.item.upper_bound) if isinstance(upper_bound, Instance): @@ -419,12 +447,13 @@ def analyze_type_type_member_access( TypeType.make_normalized(upper_bound, line=typ.line, column=typ.column), mx, override_info, + options=options, ) elif isinstance(upper_bound, TupleType): item = tuple_fallback(upper_bound) elif isinstance(upper_bound, AnyType): with mx.msg.filter_errors(): - return _analyze_member_access(name, fallback, mx, override_info) + return _analyze_member_access(name, fallback, mx, override_info, options=options) elif isinstance(typ.item, TupleType): item = tuple_fallback(typ.item) elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj(): @@ -436,7 +465,7 @@ def analyze_type_type_member_access( ignore_messages = False if item and not mx.is_operator: # See comment above for why operators are skipped - result = analyze_class_attribute_access(item, name, mx, override_info) + result = analyze_class_attribute_access(item, name, mx, override_info, options=options) if result: if not (isinstance(get_proper_type(result), AnyType) and item.type.fallback_to_any): return result @@ -447,20 +476,24 @@ def analyze_type_type_member_access( fallback = item.type.metaclass_type or fallback with mx.msg.filter_errors(filter_errors=ignore_messages): - return _analyze_member_access(name, fallback, mx, override_info) + return _analyze_member_access(name, fallback, mx, override_info, options=options) -def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> Type: +def analyze_union_member_access( + name: str, typ: UnionType, mx: MemberContext, *, options: Options +) -> Type: with mx.msg.disable_type_names(): results = [] for subtype in typ.relevant_items(): # Self types should be bound to every individual item of a union. item_mx = mx.copy_modified(self_type=subtype) - results.append(_analyze_member_access(name, subtype, item_mx)) + results.append(_analyze_member_access(name, subtype, item_mx, options=options)) return make_simplified_union(results) -def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> Type: +def analyze_none_member_access( + name: str, typ: NoneType, mx: MemberContext, *, options: Options +) -> Type: if name == "__bool__": literal_false = LiteralType(False, fallback=mx.named_type("builtins.bool")) return CallableType( @@ -471,11 +504,11 @@ def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> T fallback=mx.named_type("builtins.function"), ) else: - return _analyze_member_access(name, mx.named_type("builtins.object"), mx) + return _analyze_member_access(name, mx.named_type("builtins.object"), mx, options=options) def analyze_member_var_access( - name: str, itype: Instance, info: TypeInfo, mx: MemberContext + name: str, itype: Instance, info: TypeInfo, mx: MemberContext, *, options: Options ) -> Type: """Analyse attribute access that does not target a method. @@ -521,7 +554,7 @@ def analyze_member_var_access( if mx.is_lvalue and not mx.chk.get_final_context(): check_final_member(name, info, mx.msg, mx.context) - return analyze_var(name, v, itype, info, mx, implicit=implicit) + return analyze_var(name, v, itype, info, mx, implicit=implicit, options=options) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif isinstance(v, MypyFile): @@ -551,6 +584,7 @@ def analyze_member_var_access( self_type=mx.self_type, name=method_name, mx=mx, + options=options, ) typ = map_instance_to_supertype(itype, method.info) getattr_type = get_proper_type(expand_type_by_instance(bound_method, typ)) @@ -579,6 +613,7 @@ def analyze_member_var_access( self_type=mx.self_type, name=name, mx=mx.copy_modified(is_lvalue=False), + options=options, ) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = get_proper_type(expand_type_by_instance(bound_type, typ)) @@ -598,7 +633,7 @@ def analyze_member_var_access( mx.msg.undefined_in_superclass(name, mx.context) return AnyType(TypeOfAny.from_error) else: - return report_missing_attribute(mx.original_type, itype, name, mx) + return report_missing_attribute(mx.original_type, itype, name, mx, options=options) def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Context) -> None: @@ -609,7 +644,7 @@ def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Cont msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) -def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: +def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext, options: Options) -> Type: """Type check descriptor access. Arguments: @@ -626,7 +661,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: if isinstance(descriptor_type, UnionType): # Map the access over union types return make_simplified_union( - [analyze_descriptor_access(typ, mx) for typ in descriptor_type.items] + [analyze_descriptor_access(typ, mx, options) for typ in descriptor_type.items] ) elif not isinstance(descriptor_type, Instance): return orig_descriptor_type @@ -637,7 +672,10 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: dunder_get = descriptor_type.type.get_method("__get__") if dunder_get is None: mx.msg.fail( - message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(descriptor_type), mx.context + message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( + descriptor_type.str_with_options(options) + ), + mx.context, ) return AnyType(TypeOfAny.from_error) @@ -648,6 +686,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: self_type=descriptor_type, name="__get__", mx=mx, + options=options, ) typ = map_instance_to_supertype(descriptor_type, dunder_get.info) @@ -694,7 +733,10 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: if not isinstance(inferred_dunder_get_type, CallableType): mx.msg.fail( - message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(descriptor_type), mx.context + message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( + descriptor_type.str_with_options(options) + ), + mx.context, ) return AnyType(TypeOfAny.from_error) @@ -721,6 +763,7 @@ def analyze_var( mx: MemberContext, *, implicit: bool = False, + options: Options, ) -> Type: """Analyze access to an attribute via a Var node. @@ -799,7 +842,7 @@ def analyze_var( fullname = f"{var.info.fullname}.{name}" hook = mx.chk.plugin.get_attribute_hook(fullname) if result and not mx.is_lvalue and not implicit: - result = analyze_descriptor_access(result, mx) + result = analyze_descriptor_access(result, mx, options=options) if hook: result = hook( AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk) @@ -889,6 +932,8 @@ def analyze_class_attribute_access( mx: MemberContext, override_info: TypeInfo | None = None, original_vars: Sequence[TypeVarLikeType] | None = None, + *, + options: Options, ) -> Type | None: """Analyze access to an attribute on a class object. @@ -938,7 +983,9 @@ def analyze_class_attribute_access( check_final_member(name, info, mx.msg, mx.context) if info.is_enum and not (mx.is_lvalue or is_decorated or is_method): - enum_class_attribute_type = analyze_enum_class_attribute_access(itype, name, mx) + enum_class_attribute_type = analyze_enum_class_attribute_access( + itype, name, mx, options=options + ) if enum_class_attribute_type: return apply_class_attr_hook(mx, hook, enum_class_attribute_type) @@ -1007,7 +1054,7 @@ def analyze_class_attribute_access( t, isuper, is_classmethod, mx.self_type, original_vars=original_vars ) if not mx.is_lvalue: - result = analyze_descriptor_access(result, mx) + result = analyze_descriptor_access(result, mx, options=options) return apply_class_attr_hook(mx, hook, result) elif isinstance(node.node, Var): @@ -1061,11 +1108,11 @@ def apply_class_attr_hook( def analyze_enum_class_attribute_access( - itype: Instance, name: str, mx: MemberContext + itype: Instance, name: str, mx: MemberContext, *, options: Options ) -> Type | None: # Skip these since Enum will remove it if name in ENUM_REMOVED_PROPS: - return report_missing_attribute(mx.original_type, itype, name, mx) + return report_missing_attribute(mx.original_type, itype, name, mx, options=options) # For other names surrendered by underscores, we don't make them Enum members if name.startswith("__") and name.endswith("__") and name.replace("_", "") != "": return None @@ -1075,7 +1122,12 @@ def analyze_enum_class_attribute_access( def analyze_typeddict_access( - name: str, typ: TypedDictType, mx: MemberContext, override_info: TypeInfo | None + name: str, + typ: TypedDictType, + mx: MemberContext, + override_info: TypeInfo | None, + *, + options: Options, ) -> Type: if name == "__setitem__": if isinstance(mx.context, IndexExpr): @@ -1107,7 +1159,7 @@ def analyze_typeddict_access( fallback=mx.chk.named_type("builtins.function"), name=name, ) - return _analyze_member_access(name, typ.fallback, mx, override_info) + return _analyze_member_access(name, typ.fallback, mx, override_info, options=options) def add_class_tvars( @@ -1256,6 +1308,8 @@ def analyze_decorator_or_funcbase_access( self_type: Type | None, name: str, mx: MemberContext, + *, + options: Options, ) -> Type: """Analyzes the type behind method access. @@ -1263,7 +1317,7 @@ def analyze_decorator_or_funcbase_access( See: https://github.com/python/mypy/issues/10409 """ if isinstance(defn, Decorator): - return analyze_var(name, defn.var, itype, info, mx) + return analyze_var(name, defn.var, itype, info, mx, options=options) return bind_self( function_type(defn, mx.chk.named_type("builtins.function")), original_type=self_type ) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 2b38dd70b7ead..2bb3285629e4f 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -16,6 +16,7 @@ from mypy.meet import narrow_declared_type from mypy.messages import MessageBuilder from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, TypeInfo, Var +from mypy.options import Options from mypy.patterns import ( AsPattern, ClassPattern, @@ -104,7 +105,11 @@ class PatternChecker(PatternVisitor[PatternType]): # non_sequence_match_type_names non_sequence_match_types: list[Type] - def __init__(self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin) -> None: + options: Options + + def __init__( + self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin, options: Options + ) -> None: self.chk = chk self.msg = msg self.plugin = plugin @@ -114,6 +119,7 @@ def __init__(self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: P self.non_sequence_match_types = self.generate_types_from_names( non_sequence_match_type_names ) + self.options = options def accept(self, o: Pattern, type_context: Type) -> PatternType: self.type_context.append(type_context) @@ -458,8 +464,8 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: elif isinstance(type_info, TypeAlias): typ = type_info.target else: - if isinstance(type_info, Var): - name = str(type_info.type) + if isinstance(type_info, Var) and type_info.type is not None: + name = type_info.type.str_with_options(self.options) else: name = type_info.name self.msg.fail(message_registry.CLASS_PATTERN_TYPE_REQUIRED.format(name), o.class_ref) @@ -505,10 +511,16 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: self.msg, original_type=typ, chk=self.chk, + options=self.options, ) has_local_errors = local_errors.has_new_errors() if has_local_errors: - self.msg.fail(message_registry.MISSING_MATCH_ARGS.format(typ), o) + self.msg.fail( + message_registry.MISSING_MATCH_ARGS.format( + typ.str_with_options(self.options) + ), + o, + ) return self.early_non_match() proper_match_args_type = get_proper_type(match_args_type) @@ -566,6 +578,7 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: self.msg, original_type=new_type, chk=self.chk, + options=self.options, ) else: key_type = AnyType(TypeOfAny.from_error) @@ -573,7 +586,10 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: if has_local_errors or key_type is None: key_type = AnyType(TypeOfAny.from_error) self.msg.fail( - message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format(typ, keyword), pattern + message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format( + typ.str_with_options(self.options), keyword + ), + pattern, ) inner_type, inner_rest_type, inner_captures = self.accept(pattern, key_type) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 701a2d42ebfb1..974985d8b4fc7 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -588,7 +588,7 @@ def apply_field_accessors( return repl assert spec.field - temp_errors = Errors() + temp_errors = Errors(self.chk.options) dummy = DUMMY_FIELD_NAME + spec.field[len(spec.key) :] temp_ast: Node = parse( dummy, fnam="", module=None, options=self.chk.options, errors=temp_errors diff --git a/mypy/errors.py b/mypy/errors.py index 757f31ba1f6b9..000857e927a84 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -258,28 +258,26 @@ class Errors: def __init__( self, - show_error_context: bool = False, - show_column_numbers: bool = False, - hide_error_codes: bool = False, - pretty: bool = False, - show_error_end: bool = False, + options: Options, + *, read_source: Callable[[str], list[str] | None] | None = None, - show_absolute_path: bool = False, - many_errors_threshold: int = -1, - options: Options | None = None, + many_errors_threshold: int | None = None, ) -> None: - self.show_error_context = show_error_context - self.show_column_numbers = show_column_numbers - self.hide_error_codes = hide_error_codes - self.show_absolute_path = show_absolute_path - self.pretty = pretty - self.show_error_end = show_error_end - if show_error_end: - assert show_column_numbers, "Inconsistent formatting, must be prevented by argparse" + + self.options = options + self.show_error_context = options.show_error_context + self.show_column_numbers = options.show_column_numbers + self.hide_error_codes = options.hide_error_codes + self.show_absolute_path = options.show_absolute_path + self.pretty = options.pretty + self.show_error_end = options.show_error_end # We use fscache to read source code when showing snippets. self.read_source = read_source - self.many_errors_threshold = many_errors_threshold - self.options = options + self.many_errors_threshold = ( + many_errors_threshold + if many_errors_threshold is not None + else options.many_errors_threshold + ) self.initialize() def initialize(self) -> None: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 9a38d67f30e51..14799214ca6b1 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -264,7 +264,7 @@ def parse( if options is None: options = Options() if errors is None: - errors = Errors(hide_error_codes=options.hide_error_codes) + errors = Errors(options) raise_on_error = True errors.set_file(fnam, module, options=options) is_stub_file = fnam.endswith(".pyi") diff --git a/mypy/inspections.py b/mypy/inspections.py index d99e087b93a16..cb695a80eef29 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -247,7 +247,9 @@ def expr_type(self, expression: Expression) -> tuple[str, bool]: if expr_type is None: return self.missing_type(expression), False - type_str = format_type(expr_type, verbosity=self.verbosity) + type_str = format_type( + expr_type, self.fg_manager.manager.options, verbosity=self.verbosity + ) return self.add_prefixes(type_str, expression), True def object_type(self) -> Instance: diff --git a/mypy/main.py b/mypy/main.py index 3f5e02ec3f794..94def7c1b650d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -734,6 +734,13 @@ def add_invertible_flag( help="Disable strict Optional checks (inverse: --strict-optional)", ) + add_invertible_flag( + "--force-uppercase-builtins", + default=False, + help="Force names to be uppercase", + group=none_group, + ) + lint_group = parser.add_argument_group( title="Configuring warnings", description="Detect code that is sound but redundant or problematic.", diff --git a/mypy/messages.py b/mypy/messages.py index bbeb763e27975..4802fa9b36d07 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -52,6 +52,7 @@ reverse_builtin_aliases, ) from mypy.operators import op_methods, op_methods_to_symbols +from mypy.options import Options from mypy.subtypes import ( IS_CLASS_OR_STATIC, IS_CLASSVAR, @@ -81,6 +82,7 @@ TypeAliasType, TypedDictType, TypeOfAny, + TypeStrVisitor, TypeType, TypeVarTupleType, TypeVarType, @@ -158,6 +160,7 @@ class MessageBuilder: def __init__(self, errors: Errors, modules: dict[str, MypyFile]) -> None: self.errors = errors + self.options = errors.options self.modules = modules self._disable_type_names = [] @@ -367,7 +370,7 @@ def has_no_attr( self.fail(f'Member "{member}" is not assignable', context) elif member == "__contains__": self.fail( - f"Unsupported right operand type for in ({format_type(original_type)})", + f"Unsupported right operand type for in ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) @@ -380,19 +383,19 @@ def has_no_attr( break elif member == "__neg__": self.fail( - f"Unsupported operand type for unary - ({format_type(original_type)})", + f"Unsupported operand type for unary - ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) elif member == "__pos__": self.fail( - f"Unsupported operand type for unary + ({format_type(original_type)})", + f"Unsupported operand type for unary + ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) elif member == "__invert__": self.fail( - f"Unsupported operand type for ~ ({format_type(original_type)})", + f"Unsupported operand type for ~ ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) @@ -402,13 +405,13 @@ def has_no_attr( if isinstance(original_type, CallableType) and original_type.is_type_obj(): self.fail( "The type {} is not generic and not indexable".format( - format_type(original_type) + format_type(original_type, self.options) ), context, ) else: self.fail( - f"Value of type {format_type(original_type)} is not indexable", + f"Value of type {format_type(original_type, self.options)} is not indexable", context, code=codes.INDEX, ) @@ -416,7 +419,7 @@ def has_no_attr( # Indexed set. self.fail( "Unsupported target for indexed assignment ({})".format( - format_type(original_type) + format_type(original_type, self.options) ), context, code=codes.INDEX, @@ -430,7 +433,7 @@ def has_no_attr( self.fail("Cannot call function of unknown type", context, code=codes.OPERATOR) else: self.fail( - message_registry.NOT_CALLABLE.format(format_type(original_type)), + message_registry.NOT_CALLABLE.format(format_type(original_type, self.options)), context, code=codes.OPERATOR, ) @@ -450,7 +453,7 @@ def has_no_attr( and not module_symbol_table[member].module_public ): self.fail( - f"{format_type(original_type, module_names=True)} does not " + f"{format_type(original_type, self.options, module_names=True)} does not " f'explicitly export attribute "{member}"', context, code=codes.ATTR_DEFINED, @@ -472,7 +475,7 @@ def has_no_attr( if matches: self.fail( '{} has no attribute "{}"; maybe {}?{}'.format( - format_type(original_type), + format_type(original_type, self.options), member, pretty_seq(matches, "or"), extra, @@ -484,7 +487,7 @@ def has_no_attr( if not failed: self.fail( '{} has no attribute "{}"{}'.format( - format_type(original_type), member, extra + format_type(original_type, self.options), member, extra ), context, code=codes.ATTR_DEFINED, @@ -492,7 +495,9 @@ def has_no_attr( elif isinstance(original_type, UnionType): # The checker passes "object" in lieu of "None" for attribute # checks, so we manually convert it back. - typ_format, orig_type_format = format_type_distinctly(typ, original_type) + typ_format, orig_type_format = format_type_distinctly( + typ, original_type, options=self.options + ) if typ_format == '"object"' and any( type(item) == NoneType for item in original_type.items ): @@ -507,8 +512,8 @@ def has_no_attr( elif isinstance(original_type, TypeVarType): bound = get_proper_type(original_type.upper_bound) if isinstance(bound, UnionType): - typ_fmt, bound_fmt = format_type_distinctly(typ, bound) - original_type_fmt = format_type(original_type) + typ_fmt, bound_fmt = format_type_distinctly(typ, bound, options=self.options) + original_type_fmt = format_type(original_type, self.options) self.fail( "Item {} of the upper bound {} of type variable {} has no " 'attribute "{}"{}'.format( @@ -519,7 +524,9 @@ def has_no_attr( ) else: self.fail( - '{} has no attribute "{}"{}'.format(format_type(original_type), member, extra), + '{} has no attribute "{}"{}'.format( + format_type(original_type, self.options), member, extra + ), context, code=codes.ATTR_DEFINED, ) @@ -542,13 +549,13 @@ def unsupported_operand_types( if isinstance(left_type, str): left_str = left_type else: - left_str = format_type(left_type) + left_str = format_type(left_type, self.options) right_str = "" if isinstance(right_type, str): right_str = right_type else: - right_str = format_type(right_type) + right_str = format_type(right_type, self.options) if self.are_type_names_disabled(): msg = f"Unsupported operand types for {op} (likely involving Union)" @@ -560,11 +567,11 @@ def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None if self.are_type_names_disabled(): msg = f"Unsupported left operand type for {op} (some union)" else: - msg = f"Unsupported left operand type for {op} ({format_type(typ)})" + msg = f"Unsupported left operand type for {op} ({format_type(typ, self.options)})" self.fail(msg, context, code=codes.OPERATOR) def not_callable(self, typ: Type, context: Context) -> Type: - self.fail(message_registry.NOT_CALLABLE.format(format_type(typ)), context) + self.fail(message_registry.NOT_CALLABLE.format(format_type(typ, self.options)), context) return AnyType(TypeOfAny.from_error) def untyped_function_call(self, callee: CallableType, context: Context) -> Type: @@ -604,7 +611,7 @@ def incompatible_argument( if callee_name is not None: name = callee_name if callee.bound_args and callee.bound_args[0] is not None: - base = format_type(callee.bound_args[0]) + base = format_type(callee.bound_args[0], self.options) else: base = extract_type(name) @@ -637,7 +644,7 @@ def incompatible_argument( return codes.INDEX else: arg_type_str, callee_type_str = format_type_distinctly( - arg_type, callee.arg_types[n - 1] + arg_type, callee.arg_types[n - 1], options=self.options ) info = ( f" (expression has type {arg_type_str}, " @@ -658,7 +665,7 @@ def incompatible_argument( name = callee_name[1:-1] n -= 1 actual_type_str, expected_type_str = format_type_distinctly( - arg_type, callee.arg_types[0] + arg_type, callee.arg_types[0], options=self.options ) msg = "{} item {} has incompatible type {}; expected {}".format( name.title(), n, actual_type_str, expected_type_str @@ -672,18 +679,18 @@ def incompatible_argument( # don't increase verbosity unless there is need to do so if is_subtype(key_type, expected_key_type): - key_type_str = format_type(key_type) - expected_key_type_str = format_type(expected_key_type) + key_type_str = format_type(key_type, self.options) + expected_key_type_str = format_type(expected_key_type, self.options) else: key_type_str, expected_key_type_str = format_type_distinctly( - key_type, expected_key_type + key_type, expected_key_type, options=self.options ) if is_subtype(value_type, expected_value_type): - value_type_str = format_type(value_type) - expected_value_type_str = format_type(expected_value_type) + value_type_str = format_type(value_type, self.options) + expected_value_type_str = format_type(expected_value_type, self.options) else: value_type_str, expected_value_type_str = format_type_distinctly( - value_type, expected_value_type + value_type, expected_value_type, options=self.options ) msg = "{} entry {} has incompatible type {}: {}; expected {}: {}".format( @@ -697,21 +704,23 @@ def incompatible_argument( code = codes.DICT_ITEM elif callee_name == "": actual_type_str, expected_type_str = map( - strip_quotes, format_type_distinctly(arg_type, callee.arg_types[0]) + strip_quotes, + format_type_distinctly(arg_type, callee.arg_types[0], options=self.options), ) msg = "List comprehension has incompatible type List[{}]; expected List[{}]".format( actual_type_str, expected_type_str ) elif callee_name == "": actual_type_str, expected_type_str = map( - strip_quotes, format_type_distinctly(arg_type, callee.arg_types[0]) + strip_quotes, + format_type_distinctly(arg_type, callee.arg_types[0], options=self.options), ) msg = "Set comprehension has incompatible type Set[{}]; expected Set[{}]".format( actual_type_str, expected_type_str ) elif callee_name == "": actual_type_str, expected_type_str = format_type_distinctly( - arg_type, callee.arg_types[n - 1] + arg_type, callee.arg_types[n - 1], options=self.options ) msg = ( "{} expression in dictionary comprehension has incompatible type {}; " @@ -719,7 +728,7 @@ def incompatible_argument( ).format("Key" if n == 1 else "Value", actual_type_str, expected_type_str) elif callee_name == "": actual_type_str, expected_type_str = format_type_distinctly( - arg_type, callee.arg_types[0] + arg_type, callee.arg_types[0], options=self.options ) msg = "Generator has incompatible item type {}; expected {}".format( actual_type_str, expected_type_str @@ -733,7 +742,7 @@ def incompatible_argument( except IndexError: # Varargs callees expected_type = callee.arg_types[-1] arg_type_str, expected_type_str = format_type_distinctly( - arg_type, expected_type, bare=True + arg_type, expected_type, bare=True, options=self.options ) if arg_kind == ARG_STAR: arg_type_str = "*" + arg_type_str @@ -757,7 +766,7 @@ def incompatible_argument( arg_name = callee.arg_names[m - 1] assert arg_name is not None arg_type_str, expected_type_str = format_type_distinctly( - arg_type.items[arg_name], expected_type, bare=True + arg_type.items[arg_name], expected_type, bare=True, options=self.options ) arg_label = f'"{arg_name}"' if isinstance(outer_context, IndexExpr) and isinstance( @@ -867,7 +876,9 @@ def invalid_index_type( *, code: ErrorCode, ) -> None: - index_str, expected_str = format_type_distinctly(index_type, expected_type) + index_str, expected_str = format_type_distinctly( + index_type, expected_type, options=self.options + ) self.fail( "Invalid index type {} for {}; expected type {}".format( index_str, base_str, expected_str @@ -1037,7 +1048,7 @@ def no_variant_matches_arguments( name_str = f" of {name}" else: name_str = "" - arg_types_str = ", ".join(format_type(arg) for arg in arg_types) + arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types) num_args = len(arg_types) if num_args == 0: self.fail( @@ -1060,7 +1071,7 @@ def no_variant_matches_arguments( self.note(f"Possible overload variant{plural_s(len(overload.items))}:", context, code=code) for item in overload.items: - self.note(pretty_callable(item), context, offset=4, code=code) + self.note(pretty_callable(item, self.options), context, offset=4, code=code) def wrong_number_values_to_unpack( self, provided: int, expected: int, context: Context @@ -1081,7 +1092,7 @@ def unpacking_strings_disallowed(self, context: Context) -> None: self.fail("Unpacking a string is disallowed", context) def type_not_iterable(self, type: Type, context: Context) -> None: - self.fail(f"{format_type(type)} object is not iterable", context) + self.fail(f"{format_type(type, self.options)} object is not iterable", context) def possible_missing_await(self, context: Context) -> None: self.note('Maybe you forgot to use "await"?', context) @@ -1164,7 +1175,11 @@ def pretty_callable_or_overload( if decorator is not None: self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code) self.note( - pretty_callable(tp), context, offset=offset, allow_dups=allow_dups, code=code + pretty_callable(tp, self.options), + context, + offset=offset, + allow_dups=allow_dups, + code=code, ) elif isinstance(tp, Overloaded): self.pretty_overload( @@ -1188,7 +1203,7 @@ def argument_incompatible_with_supertype( secondary_context: Context, ) -> None: target = self.override_target(name, name_in_supertype, supertype) - arg_type_in_supertype_f = format_type_bare(arg_type_in_supertype) + arg_type_in_supertype_f = format_type_bare(arg_type_in_supertype, self.options) self.fail( 'Argument {} of "{}" is incompatible with {}; ' 'supertype defines the argument type as "{}"'.format( @@ -1240,7 +1255,9 @@ def return_type_incompatible_with_supertype( context: Context, ) -> None: target = self.override_target(name, name_in_supertype, supertype) - override_str, original_str = format_type_distinctly(override, original) + override_str, original_str = format_type_distinctly( + override, original, options=self.options + ) self.fail( 'Return type {} of "{}" incompatible with return type {} in {}'.format( override_str, name, original_str, target @@ -1287,7 +1304,7 @@ def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) self.fail("Keywords must be strings", context) else: self.fail( - f"Argument after ** must be a mapping, not {format_type(typ)}", + f"Argument after ** must be a mapping, not {format_type(typ, self.options)}", context, code=codes.ARG_TYPE, ) @@ -1308,7 +1325,7 @@ def first_argument_for_super_must_be_type(self, actual: Type, context: Context) # object. type_str = "a non-type instance" else: - type_str = format_type(actual) + type_str = format_type(actual, self.options) self.fail( f'Argument 1 for "super" must be a type object; got {type_str}', context, @@ -1380,7 +1397,7 @@ def cannot_determine_type_in_base(self, name: str, base: str, context: Context) def no_formal_self(self, name: str, item: CallableType, context: Context) -> None: self.fail( 'Attribute function "%s" with type %s does not accept self argument' - % (name, format_type(item)), + % (name, format_type(item, self.options)), context, ) @@ -1390,7 +1407,7 @@ def incompatible_self_argument( kind = "class attribute function" if is_classmethod else "attribute function" self.fail( 'Invalid self argument %s to %s "%s" with type %s' - % (format_type(arg), kind, name, format_type(sig)), + % (format_type(arg, self.options), kind, name, format_type(sig, self.options)), context, ) @@ -1483,7 +1500,7 @@ def incompatible_typevar_value( ) -> None: self.fail( message_registry.INCOMPATIBLE_TYPEVAR_VALUE.format( - typevar_name, callable_name(callee) or "function", format_type(typ) + typevar_name, callable_name(callee) or "function", format_type(typ, self.options) ), context, code=codes.TYPE_VAR, @@ -1493,7 +1510,7 @@ def dangerous_comparison(self, left: Type, right: Type, kind: str, ctx: Context) left_str = "element" if kind == "container" else "left operand" right_str = "container item" if kind == "container" else "right operand" message = "Non-overlapping {} check ({} type: {}, {} type: {})" - left_typ, right_typ = format_type_distinctly(left, right) + left_typ, right_typ = format_type_distinctly(left, right, options=self.options) self.fail( message.format(kind, left_str, left_typ, right_str, right_typ), ctx, @@ -1551,7 +1568,9 @@ def warn_both_operands_are_from_unions(self, context: Context) -> None: def warn_operand_was_from_union(self, side: str, original: Type, context: Context) -> None: self.note( - f"{side} operand is of type {format_type(original)}", context, code=codes.OPERATOR + f"{side} operand is of type {format_type(original, self.options)}", + context, + code=codes.OPERATOR, ) def operator_method_signatures_overlap( @@ -1565,7 +1584,10 @@ def operator_method_signatures_overlap( self.fail( 'Signatures of "{}" of "{}" and "{}" of {} ' "are unsafely overlapping".format( - reverse_method, reverse_class.name, forward_method, format_type(forward_class) + reverse_method, + reverse_class.name, + forward_method, + format_type(forward_class, self.options), ), context, ) @@ -1577,20 +1599,28 @@ def signatures_incompatible(self, method: str, other_method: str, context: Conte self.fail(f'Signatures of "{method}" and "{other_method}" are incompatible', context) def yield_from_invalid_operand_type(self, expr: Type, context: Context) -> Type: - text = format_type(expr) if format_type(expr) != "object" else expr + text = ( + format_type(expr, self.options) + if format_type(expr, self.options) != "object" + else expr + ) self.fail(f'"yield from" can\'t be applied to {text}', context) return AnyType(TypeOfAny.from_error) def invalid_signature(self, func_type: Type, context: Context) -> None: - self.fail(f"Invalid signature {format_type(func_type)}", context) + self.fail(f"Invalid signature {format_type(func_type, self.options)}", context) def invalid_signature_for_special_method( self, func_type: Type, context: Context, method_name: str ) -> None: - self.fail(f'Invalid signature {format_type(func_type)} for "{method_name}"', context) + self.fail( + f'Invalid signature {format_type(func_type, self.options)} for "{method_name}"', + context, + ) def reveal_type(self, typ: Type, context: Context) -> None: - self.note(f'Revealed type is "{typ}"', context) + visitor = TypeStrVisitor(options=self.options) + self.note(f'Revealed type is "{typ.accept(visitor)}"', context) def reveal_locals(self, type_map: dict[str, Type | None], context: Context) -> None: # To ensure that the output is predictable on Python < 3.6, @@ -1599,27 +1629,34 @@ def reveal_locals(self, type_map: dict[str, Type | None], context: Context) -> N if sorted_locals: self.note("Revealed local types are:", context) for k, v in sorted_locals.items(): - self.note(f" {k}: {v}", context) + visitor = TypeStrVisitor(options=self.options) + self.note(f" {k}: {v.accept(visitor) if v is not None else None}", context) else: self.note("There are no locals to reveal", context) def unsupported_type_type(self, item: Type, context: Context) -> None: - self.fail(f'Cannot instantiate type "Type[{format_type_bare(item)}]"', context) + self.fail( + f'Cannot instantiate type "Type[{format_type_bare(item, self.options)}]"', context + ) def redundant_cast(self, typ: Type, context: Context) -> None: - self.fail(f"Redundant cast to {format_type(typ)}", context, code=codes.REDUNDANT_CAST) + self.fail( + f"Redundant cast to {format_type(typ, self.options)}", + context, + code=codes.REDUNDANT_CAST, + ) def assert_type_fail(self, source_type: Type, target_type: Type, context: Context) -> None: self.fail( - f"Expression is of type {format_type(source_type)}, " - f"not {format_type(target_type)}", + f"Expression is of type {format_type(source_type, self.options)}, " + f"not {format_type(target_type, self.options)}", context, code=codes.ASSERT_TYPE, ) def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: self.fail( - f"{prefix} becomes {format_type(typ)} due to an unfollowed import", + f"{prefix} becomes {format_type(typ, self.options)} due to an unfollowed import", ctx, code=codes.NO_ANY_UNIMPORTED, ) @@ -1684,7 +1721,7 @@ def unexpected_typeddict_keys( if missing: self.fail( "Missing {} for TypedDict {}".format( - format_key_list(missing, short=True), format_type(typ) + format_key_list(missing, short=True), format_type(typ, self.options) ), context, code=codes.TYPEDDICT_ITEM, @@ -1693,7 +1730,7 @@ def unexpected_typeddict_keys( if extra: self.fail( "Extra {} for TypedDict {}".format( - format_key_list(extra, short=True), format_type(typ) + format_key_list(extra, short=True), format_type(typ, self.options) ), context, code=codes.TYPEDDICT_UNKNOWN_KEY, @@ -1739,7 +1776,9 @@ def typeddict_key_not_found( else: err_code = codes.TYPEDDICT_UNKNOWN_KEY if setitem else codes.TYPEDDICT_ITEM self.fail( - f'TypedDict {format_type(typ)} has no key "{item_name}"', context, code=err_code + f'TypedDict {format_type(typ, self.options)} has no key "{item_name}"', + context, + code=err_code, ) matches = best_matches(item_name, typ.items.keys(), n=3) if matches: @@ -1748,7 +1787,7 @@ def typeddict_key_not_found( ) def typeddict_context_ambiguous(self, types: list[TypedDictType], context: Context) -> None: - formatted_types = ", ".join(list(format_type_distinctly(*types))) + formatted_types = ", ".join(list(format_type_distinctly(*types, options=self.options))) self.fail( f"Type of TypedDict is ambiguous, none of ({formatted_types}) matches cleanly", context ) @@ -1760,7 +1799,8 @@ def typeddict_key_cannot_be_deleted( self.fail(f'TypedDict key "{item_name}" cannot be deleted', context) else: self.fail( - f'Key "{item_name}" of TypedDict {format_type(typ)} cannot be deleted', context + f'Key "{item_name}" of TypedDict {format_type(typ, self.options)} cannot be deleted', + context, ) def typeddict_setdefault_arguments_inconsistent( @@ -1768,7 +1808,7 @@ def typeddict_setdefault_arguments_inconsistent( ) -> None: msg = 'Argument 2 to "setdefault" of "TypedDict" has incompatible type {}; expected {}' self.fail( - msg.format(format_type(default), format_type(expected)), + msg.format(format_type(default, self.options), format_type(expected, self.options)), context, code=codes.TYPEDDICT_ITEM, ) @@ -1781,11 +1821,13 @@ def disallowed_any_type(self, typ: Type, context: Context) -> None: if isinstance(typ, AnyType): message = 'Expression has type "Any"' else: - message = f'Expression type contains "Any" (has type {format_type(typ)})' + message = f'Expression type contains "Any" (has type {format_type(typ, self.options)})' self.fail(message, context) def incorrectly_returning_any(self, typ: Type, context: Context) -> None: - message = f"Returning Any from function declared to return {format_type(typ)}" + message = ( + f"Returning Any from function declared to return {format_type(typ, self.options)}" + ) self.fail(message, context, code=codes.NO_ANY_RETURN) def incorrect__exit__return(self, context: Context) -> None: @@ -1812,7 +1854,8 @@ def untyped_decorated_function(self, typ: Type, context: Context) -> None: self.fail("Function is untyped after decorator transformation", context) else: self.fail( - f'Type of decorated function contains type "Any" ({format_type(typ)})', context + f'Type of decorated function contains type "Any" ({format_type(typ, self.options)})', + context, ) def typed_function_untyped_decorator(self, func_name: str, context: Context) -> None: @@ -1831,14 +1874,14 @@ def bad_proto_variance( def concrete_only_assign(self, typ: Type, context: Context) -> None: self.fail( - f"Can only assign concrete classes to a variable of type {format_type(typ)}", + f"Can only assign concrete classes to a variable of type {format_type(typ, self.options)}", context, code=codes.TYPE_ABSTRACT, ) def concrete_only_call(self, typ: Type, context: Context) -> None: self.fail( - f"Only concrete class can be given where {format_type(typ)} is expected", + f"Only concrete class can be given where {format_type(typ, self.options)} is expected", context, code=codes.TYPE_ABSTRACT, ) @@ -1864,7 +1907,8 @@ def note_call( ) -> None: self.note( '"{}.__call__" has type {}'.format( - format_type_bare(subtype), format_type(call, verbosity=1) + format_type_bare(subtype, self.options), + format_type(call, self.options, verbosity=1), ), context, code=code, @@ -2006,7 +2050,7 @@ def report_protocol_problems( or not subtype.type.defn.type_vars or not supertype.type.defn.type_vars ): - type_name = format_type(subtype, module_names=True) + type_name = format_type(subtype, self.options, module_names=True) self.note(f"Following member(s) of {type_name} have conflicts:", context, code=code) for name, got, exp in conflict_types[:MAX_ITEMS]: exp = get_proper_type(exp) @@ -2015,7 +2059,9 @@ def report_protocol_problems( got, (CallableType, Overloaded) ): self.note( - "{}: expected {}, got {}".format(name, *format_type_distinctly(exp, got)), + "{}: expected {}, got {}".format( + name, *format_type_distinctly(exp, got, options=self.options) + ), context, offset=OFFSET, code=code, @@ -2024,7 +2070,7 @@ def report_protocol_problems( self.note("Expected:", context, offset=OFFSET, code=code) if isinstance(exp, CallableType): self.note( - pretty_callable(exp, skip_self=class_obj or is_module), + pretty_callable(exp, self.options, skip_self=class_obj or is_module), context, offset=2 * OFFSET, code=code, @@ -2037,7 +2083,7 @@ def report_protocol_problems( self.note("Got:", context, offset=OFFSET, code=code) if isinstance(got, CallableType): self.note( - pretty_callable(got, skip_self=class_obj or is_module), + pretty_callable(got, self.options, skip_self=class_obj or is_module), context, offset=2 * OFFSET, code=code, @@ -2122,7 +2168,7 @@ def pretty_overload( self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code) self.note( - pretty_callable(item, skip_self=skip_self), + pretty_callable(item, self.options, skip_self=skip_self), context, offset=offset, allow_dups=allow_dups, @@ -2195,11 +2241,14 @@ def format_long_tuple_type(self, typ: TupleType) -> str: """Format very long tuple type using an ellipsis notation""" item_cnt = len(typ.items) if item_cnt > 10: - return "Tuple[{}, {}, ... <{} more items>]".format( - format_type_bare(typ.items[0]), format_type_bare(typ.items[1]), str(item_cnt - 2) + return "{}[{}, {}, ... <{} more items>]".format( + "tuple" if self.options.use_lowercase_names() else "Tuple", + format_type_bare(typ.items[0], self.options), + format_type_bare(typ.items[1], self.options), + str(item_cnt - 2), ) else: - return format_type_bare(typ) + return format_type_bare(typ, self.options) def generate_incompatible_tuple_error( self, @@ -2210,13 +2259,15 @@ def generate_incompatible_tuple_error( ) -> None: """Generate error message for individual incompatible tuple pairs""" error_cnt = 0 - notes = [] # List[str] + notes: list[str] = [] for i, (lhs_t, rhs_t) in enumerate(zip(lhs_types, rhs_types)): if not is_subtype(lhs_t, rhs_t): if error_cnt < 3: notes.append( "Expression tuple item {} has type {}; {} expected; ".format( - str(i), format_type(rhs_t), format_type(lhs_t) + str(i), + format_type(rhs_t, self.options), + format_type(lhs_t, self.options), ) ) error_cnt += 1 @@ -2288,7 +2339,11 @@ def format_callable_args( def format_type_inner( - typ: Type, verbosity: int, fullnames: set[str] | None, module_names: bool = False + typ: Type, + verbosity: int, + options: Options, + fullnames: set[str] | None, + module_names: bool = False, ) -> str: """ Convert a type to a relatively short string suitable for error messages. @@ -2299,7 +2354,7 @@ def format_type_inner( """ def format(typ: Type) -> str: - return format_type_inner(typ, verbosity, fullnames) + return format_type_inner(typ, verbosity, options, fullnames) def format_list(types: Sequence[Type]) -> str: return ", ".join(format(typ) for typ in types) @@ -2336,7 +2391,10 @@ def format_literal_value(typ: LiteralType) -> str: if itype.type.fullname == "typing._SpecialForm": # This is not a real type but used for some typing-related constructs. return "" - if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): + if itype.type.fullname in reverse_builtin_aliases and not options.use_lowercase_names(): + alias = reverse_builtin_aliases[itype.type.fullname] + base_str = alias.split(".")[-1] + elif verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): base_str = itype.type.fullname else: base_str = itype.type.name @@ -2345,11 +2403,7 @@ def format_literal_value(typ: LiteralType) -> str: return base_str elif itype.type.fullname == "builtins.tuple": item_type_str = format(itype.args[0]) - return f"Tuple[{item_type_str}, ...]" - elif itype.type.fullname in reverse_builtin_aliases: - alias = reverse_builtin_aliases[itype.type.fullname] - alias = alias.split(".")[-1] - return f"{alias}[{format_list(itype.args)}]" + return f"{'tuple' if options.use_lowercase_names() else 'Tuple'}[{item_type_str}, ...]" else: # There are type arguments. Convert the arguments to strings. return f"{base_str}[{format_list(itype.args)}]" @@ -2375,7 +2429,10 @@ def format_literal_value(typ: LiteralType) -> str: # Prefer the name of the fallback class (if not tuple), as it's more informative. if typ.partial_fallback.type.fullname != "builtins.tuple": return format(typ.partial_fallback) - s = f"Tuple[{format_list(typ.items)}]" + if options.use_lowercase_names(): + s = f"tuple[{format_list(typ.items)}]" + else: + s = f"Tuple[{format_list(typ.items)}]" return s elif isinstance(typ, TypedDictType): # If the TypedDictType is named, return the name @@ -2457,7 +2514,7 @@ def format_literal_value(typ: LiteralType) -> str: # error messages. return "overloaded function" elif isinstance(typ, UnboundType): - return str(typ) + return typ.accept(TypeStrVisitor(options=options)) elif isinstance(typ, Parameters): args = format_callable_args(typ.arg_types, typ.arg_kinds, typ.arg_names, format, verbosity) return f"[{args}]" @@ -2514,7 +2571,9 @@ def find_type_overlaps(*types: Type) -> set[str]: return overlaps -def format_type(typ: Type, verbosity: int = 0, module_names: bool = False) -> str: +def format_type( + typ: Type, options: Options, verbosity: int = 0, module_names: bool = False +) -> str: """ Convert a type to a relatively short string suitable for error messages. @@ -2525,10 +2584,12 @@ def format_type(typ: Type, verbosity: int = 0, module_names: bool = False) -> st modification of the formatted string is required, callers should use format_type_bare. """ - return quote_type_string(format_type_bare(typ, verbosity, module_names)) + return quote_type_string(format_type_bare(typ, options, verbosity, module_names)) -def format_type_bare(typ: Type, verbosity: int = 0, module_names: bool = False) -> str: +def format_type_bare( + typ: Type, options: Options, verbosity: int = 0, module_names: bool = False +) -> str: """ Convert a type to a relatively short string suitable for error messages. @@ -2540,10 +2601,10 @@ def format_type_bare(typ: Type, verbosity: int = 0, module_names: bool = False) instead. (The caller may want to use quote_type_string after processing has happened, to maintain consistent quoting in messages.) """ - return format_type_inner(typ, verbosity, find_type_overlaps(typ), module_names) + return format_type_inner(typ, verbosity, options, find_type_overlaps(typ), module_names) -def format_type_distinctly(*types: Type, bare: bool = False) -> tuple[str, ...]: +def format_type_distinctly(*types: Type, options: Options, bare: bool = False) -> tuple[str, ...]: """Jointly format types to distinct strings. Increase the verbosity of the type strings until they become distinct @@ -2558,7 +2619,8 @@ def format_type_distinctly(*types: Type, bare: bool = False) -> tuple[str, ...]: overlapping = find_type_overlaps(*types) for verbosity in range(2): strs = [ - format_type_inner(type, verbosity=verbosity, fullnames=overlapping) for type in types + format_type_inner(type, verbosity=verbosity, options=options, fullnames=overlapping) + for type in types ] if len(set(strs)) == len(strs): break @@ -2578,7 +2640,7 @@ def pretty_class_or_static_decorator(tp: CallableType) -> str | None: return None -def pretty_callable(tp: CallableType, skip_self: bool = False) -> str: +def pretty_callable(tp: CallableType, options: Options, skip_self: bool = False) -> str: """Return a nice easily-readable representation of a callable type. For example: def [T <: int] f(self, x: int, y: T) -> None @@ -2604,7 +2666,7 @@ def [T <: int] f(self, x: int, y: T) -> None name = tp.arg_names[i] if name: s += name + ": " - type_str = format_type_bare(tp.arg_types[i]) + type_str = format_type_bare(tp.arg_types[i], options) if tp.arg_kinds[i] == ARG_STAR2 and tp.unpack_kwargs: type_str = f"Unpack[{type_str}]" s += type_str @@ -2646,9 +2708,9 @@ def [T <: int] f(self, x: int, y: T) -> None s += " -> " if tp.type_guard is not None: - s += f"TypeGuard[{format_type_bare(tp.type_guard)}]" + s += f"TypeGuard[{format_type_bare(tp.type_guard, options)}]" else: - s += format_type_bare(tp.ret_type) + s += format_type_bare(tp.ret_type, options) if tp.variables: tvars = [] @@ -2659,11 +2721,12 @@ def [T <: int] f(self, x: int, y: T) -> None isinstance(upper_bound, Instance) and upper_bound.type.fullname != "builtins.object" ): - tvars.append(f"{tvar.name} <: {format_type_bare(upper_bound)}") + tvars.append(f"{tvar.name} <: {format_type_bare(upper_bound, options)}") elif tvar.values: tvars.append( "{} in ({})".format( - tvar.name, ", ".join([format_type_bare(tp) for tp in tvar.values]) + tvar.name, + ", ".join([format_type_bare(tp, options) for tp in tvar.values]), ) ) else: diff --git a/mypy/nodes.py b/mypy/nodes.py index 83d8d319f725d..0ec0d81cfbc89 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -25,6 +25,7 @@ from mypy_extensions import trait import mypy.strconv +from mypy.options import Options from mypy.util import short_type from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor @@ -173,7 +174,7 @@ def set_line( def get_nongen_builtins(python_version: tuple[int, int]) -> dict[str, str]: - # After 3.9 with pep585 generic builtins are allowed. + # After 3.9 with pep585 generic builtins are allowed return _nongen_builtins if python_version < (3, 9) else {} @@ -190,11 +191,16 @@ class Node(Context): __slots__ = () def __str__(self) -> str: - ans = self.accept(mypy.strconv.StrConv()) + ans = self.accept(mypy.strconv.StrConv(options=Options())) if ans is None: return repr(self) return ans + def str_with_options(self, options: Options) -> str: + ans = self.accept(mypy.strconv.StrConv(options=options)) + assert ans + return ans + def accept(self, visitor: NodeVisitor[T]) -> T: raise RuntimeError("Not implemented") @@ -3183,22 +3189,21 @@ def __str__(self) -> str: This includes the most important information about the type. """ - return self.dump() + options = Options() + return self.dump( + str_conv=mypy.strconv.StrConv(options=options), + type_str_conv=mypy.types.TypeStrVisitor(options=options), + ) def dump( - self, - str_conv: mypy.strconv.StrConv | None = None, - type_str_conv: mypy.types.TypeStrVisitor | None = None, + self, str_conv: mypy.strconv.StrConv, type_str_conv: mypy.types.TypeStrVisitor ) -> str: """Return a string dump of the contents of the TypeInfo.""" - if not str_conv: - str_conv = mypy.strconv.StrConv() + base: str = "" def type_str(typ: mypy.types.Type) -> str: - if type_str_conv: - return typ.accept(type_str_conv) - return str(typ) + return typ.accept(type_str_conv) head = "TypeInfo" + str_conv.format_id(self) if self.bases: diff --git a/mypy/options.py b/mypy/options.py index 077e0d4ed90ae..27fa1bc9ef134 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -346,6 +346,13 @@ def __init__(self) -> None: self.disable_bytearray_promotion = False self.disable_memoryview_promotion = False + self.force_uppercase_builtins = False + + def use_lowercase_names(self) -> bool: + if self.python_version >= (3, 9): + return not self.force_uppercase_builtins + return False + # To avoid breaking plugin compatibility, keep providing new_semantic_analyzer @property def new_semantic_analyzer(self) -> bool: diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 80c2ff3d33259..281494cb05e97 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -973,7 +973,7 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl inst_type = get_proper_type(inst_type) if isinstance(inst_type, AnyType): return ctx.default_signature # evolve(Any, ....) -> Any - inst_type_str = format_type_bare(inst_type) + inst_type_str = format_type_bare(inst_type, ctx.api.options) attrs_type, attrs_init_type = _get_attrs_cls_and_init(inst_type) if attrs_type is None or attrs_init_type is None: diff --git a/mypy/plugins/ctypes.py b/mypy/plugins/ctypes.py index edfbe506fccac..b687c40bbb097 100644 --- a/mypy/plugins/ctypes.py +++ b/mypy/plugins/ctypes.py @@ -123,7 +123,9 @@ def array_constructor_callback(ctx: mypy.plugin.FunctionContext) -> Type: ctx.api.msg.fail( "Array constructor argument {} of type {}" " is not convertible to the array element type {}".format( - arg_num, format_type(arg_type), format_type(et) + arg_num, + format_type(arg_type, ctx.api.options), + format_type(et, ctx.api.options), ), ctx.context, ) @@ -134,7 +136,9 @@ def array_constructor_callback(ctx: mypy.plugin.FunctionContext) -> Type: ctx.api.msg.fail( "Array constructor argument {} of type {}" " is not convertible to the array element type {}".format( - arg_num, format_type(arg_type), format_type(it) + arg_num, + format_type(arg_type, ctx.api.options), + format_type(it, ctx.api.options), ), ctx.context, ) @@ -209,7 +213,9 @@ def array_value_callback(ctx: mypy.plugin.AttributeContext) -> Type: else: ctx.api.msg.fail( 'Array attribute "value" is only available' - ' with element type "c_char" or "c_wchar", not {}'.format(format_type(et)), + ' with element type "c_char" or "c_wchar", not {}'.format( + format_type(et, ctx.api.options) + ), ctx.context, ) return make_simplified_union(types) @@ -232,7 +238,7 @@ def array_raw_callback(ctx: mypy.plugin.AttributeContext) -> Type: else: ctx.api.msg.fail( 'Array attribute "raw" is only available' - ' with element type "c_char", not {}'.format(format_type(et)), + ' with element type "c_char", not {}'.format(format_type(et, ctx.api.options)), ctx.context, ) return make_simplified_union(types) diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index ecfad83bde93f..a44493f900b14 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -5,6 +5,7 @@ from mypy.messages import format_type from mypy.nodes import ARG_POS, Argument, Block, ClassDef, Context, SymbolTable, TypeInfo, Var +from mypy.options import Options from mypy.plugin import CheckerPluginInterface, FunctionContext, MethodContext, MethodSigContext from mypy.plugins.common import add_method_to_class from mypy.subtypes import is_subtype @@ -142,7 +143,7 @@ def singledispatch_register_callback(ctx: MethodContext) -> Type: return register_callable elif isinstance(first_arg_type, CallableType): # TODO: do more checking for registered functions - register_function(ctx, ctx.type, first_arg_type) + register_function(ctx, ctx.type, first_arg_type, ctx.api.options) # The typeshed stubs for register say that the function returned is Callable[..., T], even # though the function returned is the same as the one passed in. We return the type of the # function so that mypy can properly type check cases where the registered function is used @@ -154,7 +155,11 @@ def singledispatch_register_callback(ctx: MethodContext) -> Type: def register_function( - ctx: PluginContext, singledispatch_obj: Instance, func: Type, register_arg: Type | None = None + ctx: PluginContext, + singledispatch_obj: Instance, + func: Type, + options: Options, + register_arg: Type | None = None, ) -> None: """Register a function""" @@ -178,7 +183,7 @@ def register_function( fail( ctx, "Dispatch type {} must be subtype of fallback function first argument {}".format( - format_type(dispatch_type), format_type(fallback_dispatch_type) + format_type(dispatch_type, options), format_type(fallback_dispatch_type, options) ), func.definition, ) @@ -201,7 +206,9 @@ def call_singledispatch_function_after_register_argument(ctx: MethodContext) -> type_args = RegisterCallableInfo(*register_callable.args) # type: ignore[arg-type] func = get_first_arg(ctx.arg_types) if func is not None: - register_function(ctx, type_args.singledispatch_obj, func, type_args.register_type) + register_function( + ctx, type_args.singledispatch_obj, func, ctx.api.options, type_args.register_type + ) # see call to register_function in the callback for register return func return ctx.default_return_type diff --git a/mypy/semanal.py b/mypy/semanal.py index 547bf4863edde..8d17c84b130b5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -717,7 +717,7 @@ def create_alias(self, tree: MypyFile, target_name: str, alias: str, name: str) target = self.named_type_or_none(target_name, []) assert target is not None # Transform List to List[Any], etc. - fix_instance_types(target, self.fail, self.note, self.options.python_version) + fix_instance_types(target, self.fail, self.note, self.options) alias_node = TypeAlias( target, alias, @@ -3535,7 +3535,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # if the expected number of arguments is non-zero, so that aliases like A = List work. # However, eagerly expanding aliases like Text = str is a nice performance optimization. no_args = isinstance(res, Instance) and not res.args # type: ignore[misc] - fix_instance_types(res, self.fail, self.note, self.options.python_version) + fix_instance_types(res, self.fail, self.note, self.options) # Aliases defined within functions can't be accessed outside # the function, since the symbol table will no longer # exist. Work around by expanding them eagerly when used. diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index cb1055a621862..a8380309d3109 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -105,7 +105,11 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: else: if old_type is not None: message = "Argument 2 to NewType(...) must be subclassable (got {})" - self.fail(message.format(format_type(old_type)), s, code=codes.VALID_NEWTYPE) + self.fail( + message.format(format_type(old_type, self.options)), + s, + code=codes.VALID_NEWTYPE, + ) # Otherwise the error was already reported. old_type = AnyType(TypeOfAny.from_error) object_type = self.api.named_type("builtins.object") diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index b9965236c3790..5d66c03aa33e9 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -141,7 +141,9 @@ def validate_args( is_error = True self.fail( message_registry.INVALID_TYPEVAR_ARG_BOUND.format( - format_type(arg), name, format_type(tvar.upper_bound) + format_type(arg, self.options), + name, + format_type(tvar.upper_bound, self.options), ), ctx, code=codes.TYPE_VAR, @@ -152,7 +154,7 @@ def validate_args( ): self.fail( "Can only replace ParamSpec with a parameter types list or" - f" another ParamSpec, got {format_type(arg)}", + f" another ParamSpec, got {format_type(arg, self.options)}", ctx, ) return is_error @@ -170,7 +172,9 @@ def visit_unpack_type(self, typ: UnpackType) -> None: # TODO: Infer something when it can't be unpacked to allow rest of # typechecking to work. - self.fail(message_registry.INVALID_UNPACK.format(proper_type), typ) + self.fail( + message_registry.INVALID_UNPACK.format(format_type(proper_type, self.options)), typ + ) def check_type_var_values( self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context diff --git a/mypy/strconv.py b/mypy/strconv.py index b2e9da5dbf6ae..e24be7fe35caa 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -7,11 +7,13 @@ from typing import TYPE_CHECKING, Any, Sequence import mypy.nodes +from mypy.options import Options from mypy.util import IdMapper, short_type from mypy.visitor import NodeVisitor if TYPE_CHECKING: import mypy.patterns + import mypy.types class StrConv(NodeVisitor[str]): @@ -26,12 +28,20 @@ class StrConv(NodeVisitor[str]): IntExpr(1))) """ - def __init__(self, show_ids: bool = False) -> None: + __slots__ = ["options", "show_ids", "id_mapper"] + + def __init__(self, *, show_ids: bool = False, options: Options) -> None: + self.options = options self.show_ids = show_ids self.id_mapper: IdMapper | None = None if show_ids: self.id_mapper = IdMapper() + def stringify_type(self, t: mypy.types.Type) -> str: + import mypy.types + + return t.accept(mypy.types.TypeStrVisitor(id_mapper=self.id_mapper, options=self.options)) + def get_id(self, o: object) -> int | None: if self.id_mapper: return self.id_mapper.id(o) @@ -168,11 +178,11 @@ def visit_class_def(self, o: mypy.nodes.ClassDef) -> str: if o.type_vars: a.insert(1, ("TypeVars", o.type_vars)) if o.metaclass: - a.insert(1, f"Metaclass({o.metaclass})") + a.insert(1, f"Metaclass({o.metaclass.accept(self)})") if o.decorators: a.insert(1, ("Decorators", o.decorators)) if o.info and o.info._promote: - a.insert(1, f"Promote({o.info._promote})") + a.insert(1, f"Promote([{','.join(self.stringify_type(p) for p in o.info._promote)}])") if o.info and o.info.tuple_type: a.insert(1, ("TupleType", [o.info.tuple_type])) if o.info and o.info.fallback_to_any: @@ -473,7 +483,7 @@ def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr) -> str: if o.values: a += [("Values", o.values)] if not mypy.types.is_named_instance(o.upper_bound, "builtins.object"): - a += [f"UpperBound({o.upper_bound})"] + a += [f"UpperBound({self.stringify_type(o.upper_bound)})"] return self.dump(a, o) def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> str: @@ -485,7 +495,7 @@ def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> str: if o.variance == mypy.nodes.CONTRAVARIANT: a += ["Variance(CONTRAVARIANT)"] if not mypy.types.is_named_instance(o.upper_bound, "builtins.object"): - a += [f"UpperBound({o.upper_bound})"] + a += [f"UpperBound({self.stringify_type(o.upper_bound)})"] return self.dump(a, o) def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> str: @@ -497,14 +507,14 @@ def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> str: if o.variance == mypy.nodes.CONTRAVARIANT: a += ["Variance(CONTRAVARIANT)"] if not mypy.types.is_named_instance(o.upper_bound, "builtins.object"): - a += [f"UpperBound({o.upper_bound})"] + a += [f"UpperBound({self.stringify_type(o.upper_bound)})"] return self.dump(a, o) def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> str: - return f"TypeAliasExpr({o.type})" + return f"TypeAliasExpr({self.stringify_type(o.type)})" def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> str: - return f"NamedTupleExpr:{o.line}({o.info.name}, {o.info.tuple_type})" + return f"NamedTupleExpr:{o.line}({o.info.name}, {self.stringify_type(o.info.tuple_type) if o.info.tuple_type is not None else None})" def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr) -> str: return f"EnumCallExpr:{o.line}({o.info.name}, {o.items})" @@ -513,7 +523,7 @@ def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr) -> str: return f"TypedDictExpr:{o.line}({o.info.name})" def visit__promote_expr(self, o: mypy.nodes.PromoteExpr) -> str: - return f"PromoteExpr:{o.line}({o.type})" + return f"PromoteExpr:{o.line}({self.stringify_type(o.type)})" def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr) -> str: return f"NewTypeExpr:{o.line}({o.name}, {self.dump([o.old_type], o)})" @@ -614,7 +624,9 @@ def dump_tagged(nodes: Sequence[object], tag: str | None, str_conv: StrConv) -> elif isinstance(n, mypy.nodes.Node): a.append(indent(n.accept(str_conv), 2)) elif isinstance(n, Type): - a.append(indent(n.accept(TypeStrVisitor(str_conv.id_mapper)), 2)) + a.append( + indent(n.accept(TypeStrVisitor(str_conv.id_mapper, options=str_conv.options)), 2) + ) elif n is not None: a.append(indent(str(n), 2)) if tag: diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ce2b3b8d88808..c13096189f7ea 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -307,7 +307,7 @@ class AnnotationPrinter(TypeStrVisitor): # TODO: Generate valid string representation for callable types. # TODO: Use short names for Instances. def __init__(self, stubgen: StubGenerator) -> None: - super().__init__() + super().__init__(options=mypy.options.Options()) self.stubgen = stubgen def visit_any(self, t: AnyType) -> str: @@ -1601,7 +1601,7 @@ def parse_source_file(mod: StubSource, mypy_options: MypyOptions) -> None: with open(mod.path, "rb") as f: data = f.read() source = mypy.util.decode_python_encoding(data) - errors = Errors() + errors = Errors(mypy_options) mod.ast = mypy.parse.parse( source, fnam=mod.path, module=mod.module, errors=errors, options=mypy_options ) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a4b572c206c83..ca25c2a870e5a 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1106,7 +1106,7 @@ def verify_overloadedfuncdef( "is inconsistent, " + message, stub, runtime, - stub_desc=str(stub.type) + f"\nInferred signature: {stub_sig}", + stub_desc=(str(stub.type)) + f"\nInferred signature: {stub_sig}", runtime_desc="def " + str(signature), ) diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 9ac033ba3bdf0..2e3744025325b 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -54,6 +54,7 @@ TypeInfo, reverse_builtin_aliases, ) +from mypy.options import Options from mypy.plugin import FunctionContext, MethodContext, Plugin from mypy.server.update import FineGrainedBuildManager from mypy.state import state @@ -735,7 +736,7 @@ def format_signature(self, sig: PyAnnotateSignature) -> str: def format_type(self, cur_module: str | None, typ: Type) -> str: if self.use_fixme and isinstance(get_proper_type(typ), AnyType): return self.use_fixme - return typ.accept(TypeFormatter(cur_module, self.graph)) + return typ.accept(TypeFormatter(cur_module, self.graph, self.manager.options)) def score_type(self, t: Type, arg_pos: bool) -> int: """Generate a score for a type that we use to pick which type to use. @@ -809,8 +810,8 @@ class TypeFormatter(TypeStrVisitor): """Visitor used to format types""" # TODO: Probably a lot - def __init__(self, module: str | None, graph: Graph) -> None: - super().__init__() + def __init__(self, module: str | None, graph: Graph, options: Options) -> None: + super().__init__(options=options) self.module = module self.graph = graph diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 177ea5be22985..52b2497f0c3ca 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -382,6 +382,7 @@ def parse_options( options.strict_optional = False options.error_summary = False options.hide_error_codes = True + options.force_uppercase_builtins = True # Allow custom python version to override testfile_pyversion. if all(flag.split("=")[0] not in ["--python-version", "-2", "--py2"] for flag in flag_list): diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 5f128283a190a..ef27f1c3f4ed7 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -125,6 +125,8 @@ def run_case_once( options.hide_error_codes = False if "abstract" not in testcase.file: options.allow_empty_bodies = not testcase.name.endswith("_no_empty") + if "lowercase" not in testcase.file: + options.force_uppercase_builtins = True if incremental_step and options.incremental: # Don't overwrite # flags: --no-incremental in incremental test cases diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 2e8b0dc9a1cd5..48e99939e6bab 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -61,6 +61,8 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None: args.append("--hide-error-codes") if "--disallow-empty-bodies" not in args: args.append("--allow-empty-bodies") + if "--no-force-uppercase-builtins" not in args: + args.append("--force-uppercase-builtins") # Type check the program. fixed = [python3_path, "-m", "mypy"] env = os.environ.copy() diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index b145d92aea6cd..ce7697142ff2c 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -41,9 +41,9 @@ def test_scc(self) -> None: assert_equal(sccs, {frozenset({"A"}), frozenset({"B", "C"}), frozenset({"D"})}) def _make_manager(self) -> BuildManager: - errors = Errors() options = Options() options.use_builtins_fixtures = True + errors = Errors(options) fscache = FileSystemCache() search_paths = SearchPaths((), (), (), ()) manager = BuildManager( diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 11e9a3c3d7e7f..0582c9ed58822 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -20,6 +20,7 @@ TypeVarExpr, Var, ) +from mypy.options import Options from mypy.server.subexpr import get_subexpressions from mypy.server.update import FineGrainedBuildManager from mypy.strconv import StrConv @@ -41,10 +42,10 @@ class ASTMergeSuite(DataSuite): def setup(self) -> None: super().setup() - self.str_conv = StrConv(show_ids=True) + self.str_conv = StrConv(show_ids=True, options=Options()) assert self.str_conv.id_mapper is not None self.id_mapper: IdMapper = self.str_conv.id_mapper - self.type_str_conv = TypeStrVisitor(self.id_mapper) + self.type_str_conv = TypeStrVisitor(self.id_mapper, options=Options()) def run_case(self, testcase: DataDrivenTestCase) -> None: name = testcase.name @@ -102,7 +103,11 @@ def build(self, source: str, testcase: DataDrivenTestCase) -> BuildResult | None options.export_types = True options.show_traceback = True options.allow_empty_bodies = True + options.force_uppercase_builtins = True main_path = os.path.join(test_temp_dir, "main") + + self.str_conv.options = options + self.type_str_conv.options = options with open(main_path, "w", encoding="utf8") as f: f.write(source) try: @@ -218,7 +223,12 @@ def dump_types( if type_map: a.append(f"## {module_id}") for expr in sorted( - type_map, key=lambda n: (n.line, short_type(n), str(n) + str(type_map[n])) + type_map, + key=lambda n: ( + n.line, + short_type(n), + n.str_with_options(self.str_conv.options) + str(type_map[n]), + ), ): typ = type_map[expr] a.append(f"{short_type(expr)}:{expr.line}: {self.format_type(typ)}") diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 6a2d1e145251c..3fc88277e01b4 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -32,6 +32,7 @@ def test_parser(testcase: DataDrivenTestCase) -> None: The argument contains the description of the test case. """ options = Options() + options.force_uppercase_builtins = True options.hide_error_codes = True if testcase.file.endswith("python310.test"): @@ -47,7 +48,7 @@ def test_parser(testcase: DataDrivenTestCase) -> None: errors=None, options=options, ) - a = str(n).split("\n") + a = n.str_with_options(options).split("\n") except CompileError as e: a = e.messages assert_string_arrays_equal( diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 02dd116553821..62ba54591d9d8 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -54,6 +54,7 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None "--no-error-summary", "--hide-error-codes", "--allow-empty-bodies", + "--force-uppercase-builtins", ] interpreter = python3_path mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}") diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index 3276f21540df1..3455f41aa20a0 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -46,6 +46,7 @@ def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Opti options.show_traceback = True options.python_version = PYTHON3_VERSION options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] + options.force_uppercase_builtins = True return options @@ -78,7 +79,7 @@ def test_semanal(testcase: DataDrivenTestCase) -> None: # output. for module in sorted(result.files.keys()): if module in testcase.test_modules: - a += str(result.files[module]).split("\n") + a += result.files[module].str_with_options(options).split("\n") except CompileError as e: a = e.messages if testcase.normalize_output: diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index c765bae120620..ba9fe8668fb44 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -40,6 +40,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None: options.semantic_analysis_only = True options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] options.show_traceback = True + options.force_uppercase_builtins = True result = build.build( sources=[BuildSource("main", None, src)], options=options, alt_lib_path=test_temp_dir ) @@ -53,7 +54,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None: t = TypeAssertTransformVisitor() t.test_only = True file = t.mypyfile(result.files[module]) - a += str(file).split("\n") + a += file.str_with_options(options).split("\n") except CompileError as e: a = e.messages if testcase.normalize_output: diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index 3f09254f081ae..4933bd3522a0f 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -35,6 +35,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: options.export_types = True options.preserve_asts = True options.allow_empty_bodies = True + options.force_uppercase_builtins = True result = build.build( sources=[BuildSource("main", None, src)], options=options, @@ -66,8 +67,11 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Include node in output. keys.append(node) - for key in sorted(keys, key=lambda n: (n.line, short_type(n), str(n) + str(map[n]))): - ts = str(map[key]).replace("*", "") # Remove erased tags + for key in sorted( + keys, + key=lambda n: (n.line, short_type(n), str(n) + map[n].str_with_options(options)), + ): + ts = map[key].str_with_options(options).replace("*", "") # Remove erased tags ts = ts.replace("__main__.", "") a.append(f"{short_type(key)}({key.line}) : {ts}") except CompileError as e: diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 6fe65675554bd..c8061ea3f401b 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -21,6 +21,7 @@ Expression, NameExpr, ) +from mypy.options import Options from mypy.plugins.common import find_shallow_matching_overload_item from mypy.state import state from mypy.subtypes import is_more_precise, is_proper_subtype, is_same_type, is_subtype @@ -124,10 +125,14 @@ def test_callable_type_with_var_args(self) -> None: assert_equal(str(c3), "def (X? =, *Y?) -> Any") def test_tuple_type(self) -> None: - assert_equal(str(TupleType([], self.fx.std_tuple)), "Tuple[]") - assert_equal(str(TupleType([self.x], self.fx.std_tuple)), "Tuple[X?]") + options = Options() + options.force_uppercase_builtins = True + assert_equal(TupleType([], self.fx.std_tuple).str_with_options(options), "Tuple[]") + assert_equal(TupleType([self.x], self.fx.std_tuple).str_with_options(options), "Tuple[X?]") assert_equal( - str(TupleType([self.x, AnyType(TypeOfAny.special_form)], self.fx.std_tuple)), + TupleType( + [self.x, AnyType(TypeOfAny.special_form)], self.fx.std_tuple + ).str_with_options(options), "Tuple[X?, Any]", ) @@ -1201,8 +1206,8 @@ def assert_meet(self, s: Type, t: Type, meet: Type) -> None: def assert_simple_meet(self, s: Type, t: Type, meet: Type) -> None: result = meet_types(s, t) - actual = str(result) - expected = str(meet) + actual = str(result.accept) + expected = str(meet.accept) assert_equal(actual, expected, f"meet({s}, {t}) == {{}} ({{}} expected)") assert is_subtype(result, s), f"{result} not subtype of {s}" assert is_subtype(result, t), f"{result} not subtype of {t}" diff --git a/mypy/typeanal.py b/mypy/typeanal.py index f3329af6207af..fc387f417bdf5 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -404,6 +404,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.fail, node.no_args, t, + self.options, unexpanded_type=t, disallow_any=disallow_any, ) @@ -419,7 +420,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.fail, self.note, disallow_any=disallow_any, - python_version=self.options.python_version, + options=self.options, use_generic_error=True, unexpanded_type=t, ) @@ -654,9 +655,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ def get_omitted_any(self, typ: Type, fullname: str | None = None) -> AnyType: disallow_any = not self.is_typeshed_stub and self.options.disallow_any_generics - return get_omitted_any( - disallow_any, self.fail, self.note, typ, self.options.python_version, fullname - ) + return get_omitted_any(disallow_any, self.fail, self.note, typ, self.options, fullname) def analyze_type_with_type_info( self, info: TypeInfo, args: Sequence[Type], ctx: Context @@ -702,7 +701,7 @@ def analyze_type_with_type_info( self.fail, self.note, disallow_any=self.options.disallow_any_generics and not self.is_typeshed_stub, - python_version=self.options.python_version, + options=self.options, ) tup = info.tuple_type @@ -717,6 +716,7 @@ def analyze_type_with_type_info( self.fail, False, ctx, + self.options, use_standard_error=True, ) return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) @@ -732,6 +732,7 @@ def analyze_type_with_type_info( self.fail, False, ctx, + self.options, use_standard_error=True, ) # Create a named TypedDictType @@ -1600,12 +1601,12 @@ def get_omitted_any( fail: MsgCallback, note: MsgCallback, orig_type: Type, - python_version: tuple[int, int], + options: Options, fullname: str | None = None, unexpanded_type: Type | None = None, ) -> AnyType: if disallow_any: - nongen_builtins = get_nongen_builtins(python_version) + nongen_builtins = get_nongen_builtins(options.python_version) if fullname in nongen_builtins: typ = orig_type # We use a dedicated error message for builtin generics (as the most common case). @@ -1617,7 +1618,7 @@ def get_omitted_any( ) else: typ = unexpanded_type or orig_type - type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ) + type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ, options) fail( message_registry.BARE_GENERIC.format(quote_type_string(type_str)), @@ -1630,7 +1631,10 @@ def get_omitted_any( ) # Ideally, we'd check whether the type is quoted or `from __future__ annotations` # is set before issuing this note - if python_version < (3, 9) and base_fullname in GENERIC_STUB_NOT_AT_RUNTIME_TYPES: + if ( + options.python_version < (3, 9) + and base_fullname in GENERIC_STUB_NOT_AT_RUNTIME_TYPES + ): # Recommend `from __future__ import annotations` or to put type in quotes # (string literal escaping) for classes not generic at runtime note( @@ -1654,7 +1658,7 @@ def fix_instance( fail: MsgCallback, note: MsgCallback, disallow_any: bool, - python_version: tuple[int, int], + options: Options, use_generic_error: bool = False, unexpanded_type: Type | None = None, ) -> None: @@ -1667,9 +1671,7 @@ def fix_instance( fullname: str | None = None else: fullname = t.type.fullname - any_type = get_omitted_any( - disallow_any, fail, note, t, python_version, fullname, unexpanded_type - ) + any_type = get_omitted_any(disallow_any, fail, note, t, options, fullname, unexpanded_type) t.args = (any_type,) * len(t.type.type_vars) return # Invalid number of type parameters. @@ -1691,6 +1693,7 @@ def expand_type_alias( fail: MsgCallback, no_args: bool, ctx: Context, + options: Options, *, unexpanded_type: Type | None = None, disallow_any: bool = False, @@ -1714,6 +1717,7 @@ def expand_type_alias( node, ctx.line, ctx.column, + options, disallow_any=disallow_any, fail=fail, unexpanded_type=unexpanded_type, @@ -1743,7 +1747,7 @@ def expand_type_alias( else: msg = f"Bad number of arguments for type alias, expected: {exp_len}, given: {act_len}" fail(msg, ctx, code=codes.TYPE_ARG) - return set_any_tvars(node, ctx.line, ctx.column, from_error=True) + return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True) # TODO: we need to check args validity w.r.t alias.alias_tvars. # Otherwise invalid instantiations will be allowed in runtime context. # Note: in type context, these will be still caught by semanal_typeargs. @@ -1764,6 +1768,7 @@ def set_any_tvars( node: TypeAlias, newline: int, newcolumn: int, + options: Options, *, from_error: bool = False, disallow_any: bool = False, @@ -1780,7 +1785,7 @@ def set_any_tvars( type_str = ( unexpanded_type.name if isinstance(unexpanded_type, UnboundType) - else format_type_bare(unexpanded_type) + else format_type_bare(unexpanded_type, options) ) else: type_str = node.name @@ -2023,24 +2028,20 @@ def make_optional_type(t: Type) -> Type: return UnionType([t, NoneType()], t.line, t.column) -def fix_instance_types( - t: Type, fail: MsgCallback, note: MsgCallback, python_version: tuple[int, int] -) -> None: +def fix_instance_types(t: Type, fail: MsgCallback, note: MsgCallback, options: Options) -> None: """Recursively fix all instance types (type argument count) in a given type. For example 'Union[Dict, List[str, int]]' will be transformed into 'Union[Dict[Any, Any], List[Any]]' in place. """ - t.accept(InstanceFixer(fail, note, python_version)) + t.accept(InstanceFixer(fail, note, options)) class InstanceFixer(TypeTraverserVisitor): - def __init__( - self, fail: MsgCallback, note: MsgCallback, python_version: tuple[int, int] - ) -> None: + def __init__(self, fail: MsgCallback, note: MsgCallback, options: Options) -> None: self.fail = fail self.note = note - self.python_version = python_version + self.options = options def visit_instance(self, typ: Instance) -> None: super().visit_instance(typ) @@ -2050,7 +2051,7 @@ def visit_instance(self, typ: Instance) -> None: self.fail, self.note, disallow_any=False, - python_version=self.python_version, + options=self.options, use_generic_error=True, ) diff --git a/mypy/types.py b/mypy/types.py index e78209be058fe..0af15f5759a45 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -33,6 +33,7 @@ FuncItem, SymbolNode, ) +from mypy.options import Options from mypy.state import state from mypy.util import IdMapper @@ -256,7 +257,10 @@ def accept(self, visitor: TypeVisitor[T]) -> T: raise RuntimeError("Not implemented") def __repr__(self) -> str: - return self.accept(TypeStrVisitor()) + return self.accept(TypeStrVisitor(options=Options())) + + def str_with_options(self, options: Options) -> str: + return self.accept(TypeStrVisitor(options=options)) def serialize(self) -> JsonDict | str: raise NotImplementedError(f"Cannot serialize {self.__class__.__name__} instance") @@ -2944,9 +2948,10 @@ class TypeStrVisitor(SyntheticTypeVisitor[str]): - Represent the NoneType type as None. """ - def __init__(self, id_mapper: IdMapper | None = None) -> None: + def __init__(self, id_mapper: IdMapper | None = None, *, options: Options) -> None: self.id_mapper = id_mapper self.any_as_dots = False + self.options = options def visit_unbound_type(self, t: UnboundType) -> str: s = t.name + "?" @@ -2988,7 +2993,7 @@ def visit_instance(self, t: Instance) -> str: if t.last_known_value and not t.args: # Instances with a literal fallback should never be generic. If they are, # something went wrong so we fall back to showing the full Instance repr. - s = f"{t.last_known_value}?" + s = f"{t.last_known_value.accept(self)}?" else: s = t.type.fullname or t.type.name or "" @@ -3136,11 +3141,12 @@ def visit_overloaded(self, t: Overloaded) -> str: def visit_tuple_type(self, t: TupleType) -> str: s = self.list_str(t.items) + tuple_name = "tuple" if self.options.use_lowercase_names() else "Tuple" if t.partial_fallback and t.partial_fallback.type: fallback_name = t.partial_fallback.type.fullname if fallback_name != "builtins.tuple": - return f"Tuple[{s}, fallback={t.partial_fallback.accept(self)}]" - return f"Tuple[{s}]" + return f"{tuple_name}[{s}, fallback={t.partial_fallback.accept(self)}]" + return f"{tuple_name}[{s}]" def visit_typeddict_type(self, t: TypedDictType) -> str: def item_str(name: str, typ: str) -> str: diff --git a/mypyc/build.py b/mypyc/build.py index 8e1ee8078c11f..5fc041e2dcf24 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -225,7 +225,7 @@ def generate_c( if compiler_options.verbose: print(f"Parsed and typechecked in {t1 - t0:.3f}s") - errors = Errors() + errors = Errors(options) modules, ctext = emitmodule.compile_modules_to_c( result, compiler_options=compiler_options, errors=errors, groups=groups ) diff --git a/mypyc/errors.py b/mypyc/errors.py index 1dd269fe25f31..8bc9b2714f75f 100644 --- a/mypyc/errors.py +++ b/mypyc/errors.py @@ -1,13 +1,14 @@ from __future__ import annotations import mypy.errors +from mypy.options import Options class Errors: - def __init__(self) -> None: + def __init__(self, options: Options) -> None: self.num_errors = 0 self.num_warnings = 0 - self._errors = mypy.errors.Errors(hide_error_codes=True) + self._errors = mypy.errors.Errors(options, hide_error_codes=True) def error(self, msg: str, path: str, line: int) -> None: self._errors.report(line, None, msg, severity="error", file=path) diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 5ab38d0f2264c..480c683aa1641 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -13,6 +13,7 @@ from mypy.errors import Errors from mypy.messages import MessageBuilder from mypy.nodes import Context, Expression +from mypy.options import Options from mypyc.ir.ops import Integer, Value from mypyc.ir.rtypes import ( c_pyssize_t_rprimitive, @@ -108,7 +109,9 @@ def tokenizer_format_call(format_str: str) -> tuple[list[str], list[FormatOp]] | """ # Creates an empty MessageBuilder here. # It wouldn't be used since the code has passed the type-checking. - specifiers = parse_format_value(format_str, EMPTY_CONTEXT, MessageBuilder(Errors(), {})) + specifiers = parse_format_value( + format_str, EMPTY_CONTEXT, MessageBuilder(Errors(Options()), {}) + ) if specifiers is None: return None format_ops = generate_format_ops(specifiers) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 8d6dd90d770d1..dc054ac9002f9 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -241,7 +241,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> groups=groups, alt_lib_path=".", ) - errors = Errors() + errors = Errors(options) ir, cfiles = emitmodule.compile_modules_to_c( result, compiler_options=compiler_options, errors=errors, groups=groups ) diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 609ffc27385ea..796811a6363c6 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -121,7 +121,7 @@ def build_ir_for_single_file2( if result.errors: raise CompileError(result.errors) - errors = Errors() + errors = Errors(options) modules = build_ir( [result.files["__main__"]], result.graph, diff --git a/test-data/unit/check-lowercase.test b/test-data/unit/check-lowercase.test new file mode 100644 index 0000000000000..93b180ed88d91 --- /dev/null +++ b/test-data/unit/check-lowercase.test @@ -0,0 +1,44 @@ + +[case testTupleLowercaseSettingOff] +# flags: --python-version 3.9 --force-uppercase-builtins +x = (3,) +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "Tuple[int]") +[builtins fixtures/tuple.pyi] + +[case testTupleLowercaseSettingOn] +# flags: --python-version 3.9 --no-force-uppercase-builtins +x = (3,) +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "tuple[int]") +[builtins fixtures/tuple.pyi] + +[case testListLowercaseSettingOff] +# flags: --python-version 3.9 --force-uppercase-builtins +x = [3] +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "List[int]") + +[case testListLowercaseSettingOn] +# flags: --python-version 3.9 --no-force-uppercase-builtins +x = [3] +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "list[int]") + +[case testDictLowercaseSettingOff] +# flags: --python-version 3.9 --force-uppercase-builtins +x = {"key": "value"} +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "Dict[str, str]") + +[case testDictLowercaseSettingOn] +# flags: --python-version 3.9 --no-force-uppercase-builtins +x = {"key": "value"} +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "dict[str, str]") + +[case testSetLowercaseSettingOff] +# flags: --python-version 3.9 --force-uppercase-builtins +x = {3} +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "Set[int]") +[builtins fixtures/set.pyi] + +[case testSetLowercaseSettingOn] +# flags: --python-version 3.9 --no-force-uppercase-builtins +x = {3} +x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "set[int]") +[builtins fixtures/set.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index d598fe13b7e96..92b9f7f04f26b 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -762,9 +762,9 @@ class Person(TypedDict): name: str age: int -def foo(x: Unpack[Person]) -> None: # E: TypedDict('__main__.Person', {'name': builtins.str, 'age': builtins.int}) cannot be unpacked (must be tuple or TypeVarTuple) +def foo(x: Unpack[Person]) -> None: # E: "Person" cannot be unpacked (must be tuple or TypeVarTuple) ... -def bar(x: int, *args: Unpack[Person]) -> None: # E: TypedDict('__main__.Person', {'name': builtins.str, 'age': builtins.int}) cannot be unpacked (must be tuple or TypeVarTuple) +def bar(x: int, *args: Unpack[Person]) -> None: # E: "Person" cannot be unpacked (must be tuple or TypeVarTuple) ... def baz(**kwargs: Unpack[Person]) -> None: # OK ... diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index a4ed905dcb9f5..d09ed87d3afcd 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1453,7 +1453,7 @@ from typing import Tuple heterogenous_tuple: Tuple[Unpack[Tuple[int, str]]] homogenous_tuple: Tuple[Unpack[Tuple[int, ...]]] -bad: Tuple[Unpack[int]] # E: builtins.int cannot be unpacked (must be tuple or TypeVarTuple) +bad: Tuple[Unpack[int]] # E: "int" cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/tuple.pyi] [case testTypeVarTuple] From fa0c6a4cc3e969fff0e5a1635e96f3ca8ae16113 Mon Sep 17 00:00:00 2001 From: Max Murin Date: Mon, 17 Apr 2023 13:47:27 -0700 Subject: [PATCH 2/4] remove unused parameters from errors --- mypy/errors.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index 000857e927a84..ab3d6d130339b 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -261,23 +261,14 @@ def __init__( options: Options, *, read_source: Callable[[str], list[str] | None] | None = None, - many_errors_threshold: int | None = None, + hide_error_codes: bool | None = None, ) -> None: - self.options = options - self.show_error_context = options.show_error_context - self.show_column_numbers = options.show_column_numbers - self.hide_error_codes = options.hide_error_codes - self.show_absolute_path = options.show_absolute_path - self.pretty = options.pretty - self.show_error_end = options.show_error_end + self.hide_error_codes = ( + hide_error_codes if hide_error_codes is not None else options.hide_error_codes + ) # We use fscache to read source code when showing snippets. self.read_source = read_source - self.many_errors_threshold = ( - many_errors_threshold - if many_errors_threshold is not None - else options.many_errors_threshold - ) self.initialize() def initialize(self) -> None: @@ -306,7 +297,7 @@ def set_ignore_prefix(self, prefix: str) -> None: self.ignore_prefix = prefix def simplify_path(self, file: str) -> str: - if self.show_absolute_path: + if self.options.show_absolute_path: return os.path.abspath(file) else: file = os.path.normpath(file) @@ -532,13 +523,13 @@ def add_error_info(self, info: ErrorInfo) -> None: self._add_error_info(file, note) def has_many_errors(self) -> bool: - if self.many_errors_threshold < 0: + if self.options.many_errors_threshold < 0: return False - if len(self.error_info_map) >= self.many_errors_threshold: + if len(self.error_info_map) >= self.options.many_errors_threshold: return True if ( sum(len(errors) for errors in self.error_info_map.values()) - >= self.many_errors_threshold + >= self.options.many_errors_threshold ): return True return False @@ -804,9 +795,9 @@ def format_messages( ) in errors: s = "" if file is not None: - if self.show_column_numbers and line >= 0 and column >= 0: + if self.options.show_column_numbers and line >= 0 and column >= 0: srcloc = f"{file}:{line}:{1 + column}" - if self.show_error_end and end_line >= 0 and end_column >= 0: + if self.options.show_error_end and end_line >= 0 and end_column >= 0: srcloc += f":{end_line}:{end_column}" elif line >= 0: srcloc = f"{file}:{line}" @@ -824,7 +815,7 @@ def format_messages( # displaying duplicate error codes. s = f"{s} [{code.code}]" a.append(s) - if self.pretty: + if self.options.pretty: # Add source code fragment and a location marker. if severity == "error" and source_lines and line > 0: source_line = source_lines[line - 1] @@ -855,7 +846,7 @@ def file_messages(self, path: str) -> list[str]: return [] self.flushed_files.add(path) source_lines = None - if self.pretty: + if self.options.pretty: assert self.read_source source_lines = self.read_source(path) return self.format_messages(self.error_info_map[path], source_lines) @@ -896,7 +887,7 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: for e in errors: # Report module import context, if different from previous message. - if not self.show_error_context: + if not self.options.show_error_context: pass elif e.import_ctx != prev_import_context: last = len(e.import_ctx) - 1 @@ -921,7 +912,7 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: file = self.simplify_path(e.file) # Report context within a source file. - if not self.show_error_context: + if not self.options.show_error_context: pass elif e.function_or_member != prev_function_or_member or e.type != prev_type: if e.function_or_member is None: From ddb105e3b4abcee234581d3a76103e0fbad9f2bb Mon Sep 17 00:00:00 2001 From: Max Murin Date: Tue, 18 Apr 2023 01:51:48 -0700 Subject: [PATCH 3/4] fix error introduced in testtypes.py --- mypy/test/testtypes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index c8061ea3f401b..601cdf27466ed 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -1206,8 +1206,8 @@ def assert_meet(self, s: Type, t: Type, meet: Type) -> None: def assert_simple_meet(self, s: Type, t: Type, meet: Type) -> None: result = meet_types(s, t) - actual = str(result.accept) - expected = str(meet.accept) + actual = str(result) + expected = str(meet) assert_equal(actual, expected, f"meet({s}, {t}) == {{}} ({{}} expected)") assert is_subtype(result, s), f"{result} not subtype of {s}" assert is_subtype(result, t), f"{result} not subtype of {t}" From d19637f3cda6a6f9765071aab78589c4d462d6fe Mon Sep 17 00:00:00 2001 From: Max Murin Date: Tue, 18 Apr 2023 17:44:37 -0700 Subject: [PATCH 4/4] use member context instead of passing options around, suppress help --- mypy/checker.py | 6 +- mypy/checkexpr.py | 7 --- mypy/checkmember.py | 140 ++++++++++++++----------------------------- mypy/checkpattern.py | 2 - mypy/main.py | 5 +- 5 files changed, 48 insertions(+), 112 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 29ff7d4b7c7a8..d0a89ed78276a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -688,7 +688,6 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: msg=self.msg, original_type=inner_type, chk=self, - options=self.options, ) ) if isinstance(inner_call, CallableType): @@ -3972,7 +3971,7 @@ def check_member_assignment( msg=self.msg, chk=self, ) - get_type = analyze_descriptor_access(attribute_type, mx, self.options) + get_type = analyze_descriptor_access(attribute_type, mx) if not attribute_type.type.has_readable_member("__set__"): # If there is no __set__, we type-check that the assigned value matches # the return type of __get__. This doesn't match the python semantics, @@ -3998,7 +3997,6 @@ def check_member_assignment( self_type=attribute_type, name="__set__", mx=mx, - options=self.options, ) typ = map_instance_to_supertype(attribute_type, dunder_set.info) dunder_set_type = expand_type_by_instance(bound_method, typ) @@ -5713,7 +5711,6 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None: original_type=new_parent_type, chk=self, in_literal_context=False, - options=self.options, ) if w.has_new_errors(): return None @@ -6716,7 +6713,6 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: # This is not a real attribute lookup so don't mess with deferring nodes. no_deferral=True, module_symbol_table=module_symbol_table, - options=self.options, ) return not watcher.has_new_errors() diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 95fe8e58f83ae..0a8dedb1342aa 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1244,7 +1244,6 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str chk=self.chk, in_literal_context=self.is_literal_context(), self_type=typ, - options=self.chk.options, ) narrowed = self.narrow_type_from_binder(e.callee, item, skip_non_overlapping=True) if narrowed is None: @@ -1320,7 +1319,6 @@ def check_call( original_type=callee, chk=self.chk, in_literal_context=self.is_literal_context(), - options=self.chk.options, ) callable_name = callee.type.fullname + ".__call__" # Apply method signature hook, if one exists @@ -2804,7 +2802,6 @@ def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type in_literal_context=self.is_literal_context(), module_symbol_table=module_symbol_table, is_self=is_self, - options=self.chk.options, ) return member_type @@ -2827,7 +2824,6 @@ def analyze_external_member_access( original_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), - options=self.chk.options, ) def is_literal_context(self) -> bool: @@ -3217,7 +3213,6 @@ def check_method_call_by_name( original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), - options=self.chk.options, ) return self.check_method_call(method, base_type, method_type, args, arg_kinds, context) @@ -3311,7 +3306,6 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None: msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), - options=self.chk.options, ) return None if w.has_new_errors() else member @@ -4572,7 +4566,6 @@ def visit_super_expr(self, e: SuperExpr) -> Type: msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), - options=self.chk.options, ) assert False, "unreachable" diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 6ffde770663ae..c2c6b3555805f 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -34,7 +34,6 @@ Var, is_final_node, ) -from mypy.options import Options from mypy.plugin import AttributeContext from mypy.typeops import ( bind_self, @@ -162,7 +161,6 @@ def analyze_member_access( module_symbol_table: SymbolTable | None = None, no_deferral: bool = False, is_self: bool = False, - options: Options, ) -> Type: """Return the type of attribute 'name' of 'typ'. @@ -200,7 +198,7 @@ def analyze_member_access( no_deferral=no_deferral, is_self=is_self, ) - result = _analyze_member_access(name, typ, mx, override_info, options=options) + result = _analyze_member_access(name, typ, mx, override_info) possible_literal = get_proper_type(result) if ( in_literal_context @@ -213,58 +211,46 @@ def analyze_member_access( def _analyze_member_access( - name: str, - typ: Type, - mx: MemberContext, - override_info: TypeInfo | None = None, - *, - options: Options, + name: str, typ: Type, mx: MemberContext, override_info: TypeInfo | None = None ) -> Type: # TODO: This and following functions share some logic with subtypes.find_member; # consider refactoring. typ = get_proper_type(typ) if isinstance(typ, Instance): - return analyze_instance_member_access(name, typ, mx, override_info, options=options) + return analyze_instance_member_access(name, typ, mx, override_info) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType(TypeOfAny.from_another_any, source_any=typ) elif isinstance(typ, UnionType): - return analyze_union_member_access(name, typ, mx, options=options) + return analyze_union_member_access(name, typ, mx) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): - return analyze_type_callable_member_access(name, typ, mx, options=options) + return analyze_type_callable_member_access(name, typ, mx) elif isinstance(typ, TypeType): - return analyze_type_type_member_access(name, typ, mx, override_info, options=options) + return analyze_type_type_member_access(name, typ, mx, override_info) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. - return _analyze_member_access( - name, tuple_fallback(typ), mx, override_info, options=options - ) + return _analyze_member_access(name, tuple_fallback(typ), mx, override_info) elif isinstance(typ, (LiteralType, FunctionLike)): # Actually look up from the fallback instance type. - return _analyze_member_access(name, typ.fallback, mx, override_info, options=options) + return _analyze_member_access(name, typ.fallback, mx, override_info) elif isinstance(typ, TypedDictType): - return analyze_typeddict_access(name, typ, mx, override_info, options=options) + return analyze_typeddict_access(name, typ, mx, override_info) elif isinstance(typ, NoneType): - return analyze_none_member_access(name, typ, mx, options=options) + return analyze_none_member_access(name, typ, mx) elif isinstance(typ, TypeVarLikeType): if isinstance(typ, TypeVarType) and typ.values: return _analyze_member_access( - name, make_simplified_union(typ.values), mx, override_info, options=options + name, make_simplified_union(typ.values), mx, override_info ) - return _analyze_member_access(name, typ.upper_bound, mx, override_info, options=options) + return _analyze_member_access(name, typ.upper_bound, mx, override_info) elif isinstance(typ, DeletedType): mx.msg.deleted_as_rvalue(typ, mx.context) return AnyType(TypeOfAny.from_error) - return report_missing_attribute(mx.original_type, typ, name, mx, options=options) + return report_missing_attribute(mx.original_type, typ, name, mx) def may_be_awaitable_attribute( - name: str, - typ: Type, - mx: MemberContext, - override_info: TypeInfo | None = None, - *, - options: Options, + name: str, typ: Type, mx: MemberContext, override_info: TypeInfo | None = None ) -> bool: """Check if the given type has the attribute when awaited.""" if mx.chk.checking_missing_await: @@ -274,7 +260,7 @@ def may_be_awaitable_attribute( aw_type = mx.chk.get_precise_awaitable_type(typ, local_errors) if aw_type is None: return False - _ = _analyze_member_access(name, aw_type, mx, override_info, options=options) + _ = _analyze_member_access(name, aw_type, mx, override_info) return not local_errors.has_new_errors() @@ -284,12 +270,10 @@ def report_missing_attribute( name: str, mx: MemberContext, override_info: TypeInfo | None = None, - *, - options: Options, ) -> Type: res_type = mx.msg.has_no_attr(original_type, typ, name, mx.context, mx.module_symbol_table) if not mx.msg.prefer_simple_messages(): - if may_be_awaitable_attribute(name, typ, mx, override_info, options=options): + if may_be_awaitable_attribute(name, typ, mx, override_info): mx.msg.possible_missing_await(mx.context) return res_type @@ -299,12 +283,7 @@ def report_missing_attribute( def analyze_instance_member_access( - name: str, - typ: Instance, - mx: MemberContext, - override_info: TypeInfo | None, - *, - options: Options, + name: str, typ: Instance, mx: MemberContext, override_info: TypeInfo | None ) -> Type: if name == "__init__" and not mx.is_super: # Accessing __init__ in statically typed code would compromise @@ -335,7 +314,7 @@ def analyze_instance_member_access( assert isinstance(method, OverloadedFuncDef) first_item = method.items[0] assert isinstance(first_item, Decorator) - return analyze_var(name, first_item.var, typ, info, mx, options=options) + return analyze_var(name, first_item.var, typ, info, mx) if mx.is_lvalue: mx.msg.cant_assign_to_method(mx.context) signature = function_type(method, mx.named_type("builtins.function")) @@ -361,7 +340,7 @@ def analyze_instance_member_access( return member_type else: # Not a method. - return analyze_member_var_access(name, typ, info, mx, options=options) + return analyze_member_var_access(name, typ, info, mx) def validate_super_call(node: FuncBase, mx: MemberContext) -> None: @@ -383,9 +362,7 @@ def validate_super_call(node: FuncBase, mx: MemberContext) -> None: mx.msg.unsafe_super(node.name, node.info.name, mx.context) -def analyze_type_callable_member_access( - name: str, typ: FunctionLike, mx: MemberContext, *, options: Options -) -> Type: +def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: MemberContext) -> Type: # Class attribute. # TODO super? ret_type = typ.items[0].ret_type @@ -411,23 +388,18 @@ def analyze_type_callable_member_access( # See https://github.com/python/mypy/pull/1787 for more info. # TODO: do not rely on same type variables being present in all constructor overloads. result = analyze_class_attribute_access( - ret_type, name, mx, original_vars=typ.items[0].variables, options=options + ret_type, name, mx, original_vars=typ.items[0].variables ) if result: return result # Look up from the 'type' type. - return _analyze_member_access(name, typ.fallback, mx, options=options) + return _analyze_member_access(name, typ.fallback, mx) else: assert False, f"Unexpected type {ret_type!r}" def analyze_type_type_member_access( - name: str, - typ: TypeType, - mx: MemberContext, - override_info: TypeInfo | None, - *, - options: Options, + name: str, typ: TypeType, mx: MemberContext, override_info: TypeInfo | None ) -> Type: # Similar to analyze_type_callable_attribute_access. item = None @@ -436,7 +408,7 @@ def analyze_type_type_member_access( item = typ.item elif isinstance(typ.item, AnyType): with mx.msg.filter_errors(): - return _analyze_member_access(name, fallback, mx, override_info, options=options) + return _analyze_member_access(name, fallback, mx, override_info) elif isinstance(typ.item, TypeVarType): upper_bound = get_proper_type(typ.item.upper_bound) if isinstance(upper_bound, Instance): @@ -447,13 +419,12 @@ def analyze_type_type_member_access( TypeType.make_normalized(upper_bound, line=typ.line, column=typ.column), mx, override_info, - options=options, ) elif isinstance(upper_bound, TupleType): item = tuple_fallback(upper_bound) elif isinstance(upper_bound, AnyType): with mx.msg.filter_errors(): - return _analyze_member_access(name, fallback, mx, override_info, options=options) + return _analyze_member_access(name, fallback, mx, override_info) elif isinstance(typ.item, TupleType): item = tuple_fallback(typ.item) elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj(): @@ -465,7 +436,7 @@ def analyze_type_type_member_access( ignore_messages = False if item and not mx.is_operator: # See comment above for why operators are skipped - result = analyze_class_attribute_access(item, name, mx, override_info, options=options) + result = analyze_class_attribute_access(item, name, mx, override_info) if result: if not (isinstance(get_proper_type(result), AnyType) and item.type.fallback_to_any): return result @@ -476,24 +447,20 @@ def analyze_type_type_member_access( fallback = item.type.metaclass_type or fallback with mx.msg.filter_errors(filter_errors=ignore_messages): - return _analyze_member_access(name, fallback, mx, override_info, options=options) + return _analyze_member_access(name, fallback, mx, override_info) -def analyze_union_member_access( - name: str, typ: UnionType, mx: MemberContext, *, options: Options -) -> Type: +def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> Type: with mx.msg.disable_type_names(): results = [] for subtype in typ.relevant_items(): # Self types should be bound to every individual item of a union. item_mx = mx.copy_modified(self_type=subtype) - results.append(_analyze_member_access(name, subtype, item_mx, options=options)) + results.append(_analyze_member_access(name, subtype, item_mx)) return make_simplified_union(results) -def analyze_none_member_access( - name: str, typ: NoneType, mx: MemberContext, *, options: Options -) -> Type: +def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> Type: if name == "__bool__": literal_false = LiteralType(False, fallback=mx.named_type("builtins.bool")) return CallableType( @@ -504,11 +471,11 @@ def analyze_none_member_access( fallback=mx.named_type("builtins.function"), ) else: - return _analyze_member_access(name, mx.named_type("builtins.object"), mx, options=options) + return _analyze_member_access(name, mx.named_type("builtins.object"), mx) def analyze_member_var_access( - name: str, itype: Instance, info: TypeInfo, mx: MemberContext, *, options: Options + name: str, itype: Instance, info: TypeInfo, mx: MemberContext ) -> Type: """Analyse attribute access that does not target a method. @@ -554,7 +521,7 @@ def analyze_member_var_access( if mx.is_lvalue and not mx.chk.get_final_context(): check_final_member(name, info, mx.msg, mx.context) - return analyze_var(name, v, itype, info, mx, implicit=implicit, options=options) + return analyze_var(name, v, itype, info, mx, implicit=implicit) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif isinstance(v, MypyFile): @@ -584,7 +551,6 @@ def analyze_member_var_access( self_type=mx.self_type, name=method_name, mx=mx, - options=options, ) typ = map_instance_to_supertype(itype, method.info) getattr_type = get_proper_type(expand_type_by_instance(bound_method, typ)) @@ -613,7 +579,6 @@ def analyze_member_var_access( self_type=mx.self_type, name=name, mx=mx.copy_modified(is_lvalue=False), - options=options, ) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = get_proper_type(expand_type_by_instance(bound_type, typ)) @@ -633,7 +598,7 @@ def analyze_member_var_access( mx.msg.undefined_in_superclass(name, mx.context) return AnyType(TypeOfAny.from_error) else: - return report_missing_attribute(mx.original_type, itype, name, mx, options=options) + return report_missing_attribute(mx.original_type, itype, name, mx) def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Context) -> None: @@ -644,7 +609,7 @@ def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Cont msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) -def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext, options: Options) -> Type: +def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: """Type check descriptor access. Arguments: @@ -661,7 +626,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext, options: if isinstance(descriptor_type, UnionType): # Map the access over union types return make_simplified_union( - [analyze_descriptor_access(typ, mx, options) for typ in descriptor_type.items] + [analyze_descriptor_access(typ, mx) for typ in descriptor_type.items] ) elif not isinstance(descriptor_type, Instance): return orig_descriptor_type @@ -673,7 +638,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext, options: if dunder_get is None: mx.msg.fail( message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( - descriptor_type.str_with_options(options) + descriptor_type.str_with_options(mx.msg.options) ), mx.context, ) @@ -686,7 +651,6 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext, options: self_type=descriptor_type, name="__get__", mx=mx, - options=options, ) typ = map_instance_to_supertype(descriptor_type, dunder_get.info) @@ -734,7 +698,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext, options: if not isinstance(inferred_dunder_get_type, CallableType): mx.msg.fail( message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( - descriptor_type.str_with_options(options) + descriptor_type.str_with_options(mx.msg.options) ), mx.context, ) @@ -763,7 +727,6 @@ def analyze_var( mx: MemberContext, *, implicit: bool = False, - options: Options, ) -> Type: """Analyze access to an attribute via a Var node. @@ -842,7 +805,7 @@ def analyze_var( fullname = f"{var.info.fullname}.{name}" hook = mx.chk.plugin.get_attribute_hook(fullname) if result and not mx.is_lvalue and not implicit: - result = analyze_descriptor_access(result, mx, options=options) + result = analyze_descriptor_access(result, mx) if hook: result = hook( AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk) @@ -932,8 +895,6 @@ def analyze_class_attribute_access( mx: MemberContext, override_info: TypeInfo | None = None, original_vars: Sequence[TypeVarLikeType] | None = None, - *, - options: Options, ) -> Type | None: """Analyze access to an attribute on a class object. @@ -983,9 +944,7 @@ def analyze_class_attribute_access( check_final_member(name, info, mx.msg, mx.context) if info.is_enum and not (mx.is_lvalue or is_decorated or is_method): - enum_class_attribute_type = analyze_enum_class_attribute_access( - itype, name, mx, options=options - ) + enum_class_attribute_type = analyze_enum_class_attribute_access(itype, name, mx) if enum_class_attribute_type: return apply_class_attr_hook(mx, hook, enum_class_attribute_type) @@ -1054,7 +1013,7 @@ def analyze_class_attribute_access( t, isuper, is_classmethod, mx.self_type, original_vars=original_vars ) if not mx.is_lvalue: - result = analyze_descriptor_access(result, mx, options=options) + result = analyze_descriptor_access(result, mx) return apply_class_attr_hook(mx, hook, result) elif isinstance(node.node, Var): @@ -1108,11 +1067,11 @@ def apply_class_attr_hook( def analyze_enum_class_attribute_access( - itype: Instance, name: str, mx: MemberContext, *, options: Options + itype: Instance, name: str, mx: MemberContext ) -> Type | None: # Skip these since Enum will remove it if name in ENUM_REMOVED_PROPS: - return report_missing_attribute(mx.original_type, itype, name, mx, options=options) + return report_missing_attribute(mx.original_type, itype, name, mx) # For other names surrendered by underscores, we don't make them Enum members if name.startswith("__") and name.endswith("__") and name.replace("_", "") != "": return None @@ -1122,12 +1081,7 @@ def analyze_enum_class_attribute_access( def analyze_typeddict_access( - name: str, - typ: TypedDictType, - mx: MemberContext, - override_info: TypeInfo | None, - *, - options: Options, + name: str, typ: TypedDictType, mx: MemberContext, override_info: TypeInfo | None ) -> Type: if name == "__setitem__": if isinstance(mx.context, IndexExpr): @@ -1159,7 +1113,7 @@ def analyze_typeddict_access( fallback=mx.chk.named_type("builtins.function"), name=name, ) - return _analyze_member_access(name, typ.fallback, mx, override_info, options=options) + return _analyze_member_access(name, typ.fallback, mx, override_info) def add_class_tvars( @@ -1308,8 +1262,6 @@ def analyze_decorator_or_funcbase_access( self_type: Type | None, name: str, mx: MemberContext, - *, - options: Options, ) -> Type: """Analyzes the type behind method access. @@ -1317,7 +1269,7 @@ def analyze_decorator_or_funcbase_access( See: https://github.com/python/mypy/issues/10409 """ if isinstance(defn, Decorator): - return analyze_var(name, defn.var, itype, info, mx, options=options) + return analyze_var(name, defn.var, itype, info, mx) return bind_self( function_type(defn, mx.chk.named_type("builtins.function")), original_type=self_type ) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 2bb3285629e4f..e132a23ff55f3 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -511,7 +511,6 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: self.msg, original_type=typ, chk=self.chk, - options=self.options, ) has_local_errors = local_errors.has_new_errors() if has_local_errors: @@ -578,7 +577,6 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: self.msg, original_type=new_type, chk=self.chk, - options=self.options, ) else: key_type = AnyType(TypeOfAny.from_error) diff --git a/mypy/main.py b/mypy/main.py index 94def7c1b650d..6b217e01223f7 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -735,10 +735,7 @@ def add_invertible_flag( ) add_invertible_flag( - "--force-uppercase-builtins", - default=False, - help="Force names to be uppercase", - group=none_group, + "--force-uppercase-builtins", default=False, help=argparse.SUPPRESS, group=none_group ) lint_group = parser.add_argument_group(