Skip to content

Commit 768ba66

Browse files
authored
Make messages use builtin types instead of typing's types (#15070)
Adds a new option , `use_lowercase_builtins()`, to choose whether to report builtin types (`tuple`, `list`, `set`, `frozenset`) as the builtins instead of the uppercased versions in `typing`. This option is only set to `True` for python versions that are at least 3.9. Pipes options all the way through to the messages module. This work revealed a few places that types are printed with `str()` instead of using `format_type`, which has been changed. This does change reporting overall, though this should make messages more consistent. Adds a hidden flag `--force-uppercase-builtins` that sets the option to `True` regardless of python version. This allows us to have tests that are independent of version.
1 parent 3d9661c commit 768ba66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+489
-261
lines changed

mypy/build.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,7 @@ def _build(
235235

236236
source_set = BuildSourceSet(sources)
237237
cached_read = fscache.read
238-
errors = Errors(
239-
options.show_error_context,
240-
options.show_column_numbers,
241-
options.hide_error_codes,
242-
options.pretty,
243-
options.show_error_end,
244-
lambda path: read_py_file(path, cached_read),
245-
options.show_absolute_path,
246-
options.many_errors_threshold,
247-
options,
248-
)
238+
errors = Errors(options, read_source=lambda path: read_py_file(path, cached_read))
249239
plugin, snapshot = load_plugins(options, errors, stdout, extra_plugins)
250240

251241
# Add catch-all .gitignore to cache dir if we created it

mypy/checker.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ def __init__(
419419
self.expr_checker = mypy.checkexpr.ExpressionChecker(
420420
self, self.msg, self.plugin, per_line_checking_time_ns
421421
)
422-
self.pattern_checker = PatternChecker(self, self.msg, self.plugin)
422+
self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options)
423423

424424
@property
425425
def type_context(self) -> list[Type | None]:
@@ -483,7 +483,9 @@ def check_first_pass(self) -> None:
483483
"typing.Sequence", [self.named_type("builtins.str")]
484484
)
485485
if not is_subtype(all_.type, seq_str):
486-
str_seq_s, all_s = format_type_distinctly(seq_str, all_.type)
486+
str_seq_s, all_s = format_type_distinctly(
487+
seq_str, all_.type, options=self.options
488+
)
487489
self.fail(
488490
message_registry.ALL_MUST_BE_SEQ_STR.format(str_seq_s, all_s), all_node
489491
)
@@ -1178,7 +1180,8 @@ def check_func_def(
11781180
msg = None
11791181
elif typ.arg_names[i] in {"self", "cls"}:
11801182
msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
1181-
erased, ref_type
1183+
erased.str_with_options(self.options),
1184+
ref_type.str_with_options(self.options),
11821185
)
11831186
else:
11841187
msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
@@ -1323,7 +1326,7 @@ def check_unbound_return_typevar(self, typ: CallableType) -> None:
13231326
):
13241327
self.note(
13251328
"Consider using the upper bound "
1326-
f"{format_type(typ.ret_type.upper_bound)} instead",
1329+
f"{format_type(typ.ret_type.upper_bound, self.options)} instead",
13271330
context=typ.ret_type,
13281331
)
13291332

@@ -1430,7 +1433,9 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None:
14301433
get_proper_type(bound_type.ret_type), (AnyType, Instance, TupleType, UninhabitedType)
14311434
):
14321435
self.fail(
1433-
message_registry.NON_INSTANCE_NEW_TYPE.format(format_type(bound_type.ret_type)),
1436+
message_registry.NON_INSTANCE_NEW_TYPE.format(
1437+
format_type(bound_type.ret_type, self.options)
1438+
),
14341439
fdef,
14351440
)
14361441
else:
@@ -2351,7 +2356,10 @@ class Baz(int, Foo, Bar, enum.Flag): ...
23512356
enum_base = base
23522357
continue
23532358
elif enum_base is not None and not base.type.is_enum:
2354-
self.fail(f'No non-enum mixin classes are allowed after "{enum_base}"', defn)
2359+
self.fail(
2360+
f'No non-enum mixin classes are allowed after "{enum_base.str_with_options(self.options)}"',
2361+
defn,
2362+
)
23552363
break
23562364

23572365
def check_enum_new(self, defn: ClassDef) -> None:
@@ -2376,7 +2384,7 @@ def has_new_method(info: TypeInfo) -> bool:
23762384
if candidate and has_new:
23772385
self.fail(
23782386
"Only a single data type mixin is allowed for Enum subtypes, "
2379-
'found extra "{}"'.format(base),
2387+
'found extra "{}"'.format(base.str_with_options(self.options)),
23802388
defn,
23812389
)
23822390
elif candidate:
@@ -3978,7 +3986,12 @@ def check_member_assignment(
39783986

39793987
dunder_set = attribute_type.type.get_method("__set__")
39803988
if dunder_set is None:
3981-
self.fail(message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(attribute_type), context)
3989+
self.fail(
3990+
message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(
3991+
attribute_type.str_with_options(self.options)
3992+
),
3993+
context,
3994+
)
39823995
return AnyType(TypeOfAny.from_error), get_type, False
39833996

39843997
bound_method = analyze_decorator_or_funcbase_access(
@@ -4132,7 +4145,9 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> None:
41324145
if error_note_and_code:
41334146
error_note, code = error_note_and_code
41344147
self.fail(
4135-
message_registry.TYPE_MUST_BE_USED.format(format_type(expr_type)), s, code=code
4148+
message_registry.TYPE_MUST_BE_USED.format(format_type(expr_type, self.options)),
4149+
s,
4150+
code=code,
41364151
)
41374152
self.note(error_note, s, code=code)
41384153

@@ -4962,7 +4977,9 @@ def _make_fake_typeinfo_and_full_name(
49624977
# We use the pretty_names_list for error messages but can't
49634978
# use it for the real name that goes into the symbol table
49644979
# because it can have dots in it.
4965-
pretty_names_list = pretty_seq(format_type_distinctly(*base_classes, bare=True), "and")
4980+
pretty_names_list = pretty_seq(
4981+
format_type_distinctly(*base_classes, options=self.options, bare=True), "and"
4982+
)
49664983
try:
49674984
info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module)
49684985
with self.msg.filter_errors() as local_errors:
@@ -4999,7 +5016,7 @@ def intersect_instance_callable(self, typ: Instance, callable_type: CallableType
49995016
gen_name = gen_unique_name(f"<callable subtype of {typ.type.name}>", cur_module.names)
50005017

50015018
# Synthesize a fake TypeInfo
5002-
short_name = format_type_bare(typ)
5019+
short_name = format_type_bare(typ, self.options)
50035020
cdef, info = self.make_fake_typeinfo(cur_module.fullname, gen_name, short_name, [typ])
50045021

50055022
# Build up a fake FuncDef so we can populate the symbol table.
@@ -5205,7 +5222,7 @@ def _check_for_truthy_type(self, t: Type, expr: Expression) -> None:
52055222
return
52065223

52075224
def format_expr_type() -> str:
5208-
typ = format_type(t)
5225+
typ = format_type(t, self.options)
52095226
if isinstance(expr, MemberExpr):
52105227
return f'Member "{expr.name}" has type {typ}'
52115228
elif isinstance(expr, RefExpr) and expr.fullname:
@@ -5220,14 +5237,16 @@ def format_expr_type() -> str:
52205237
return f"Expression has type {typ}"
52215238

52225239
if isinstance(t, FunctionLike):
5223-
self.fail(message_registry.FUNCTION_ALWAYS_TRUE.format(format_type(t)), expr)
5240+
self.fail(
5241+
message_registry.FUNCTION_ALWAYS_TRUE.format(format_type(t, self.options)), expr
5242+
)
52245243
elif isinstance(t, UnionType):
52255244
self.fail(message_registry.TYPE_ALWAYS_TRUE_UNIONTYPE.format(format_expr_type()), expr)
52265245
elif isinstance(t, Instance) and t.type.fullname == "typing.Iterable":
52275246
_, info = self.make_fake_typeinfo("typing", "Collection", "Collection", [])
52285247
self.fail(
52295248
message_registry.ITERABLE_ALWAYS_TRUE.format(
5230-
format_expr_type(), format_type(Instance(info, t.args))
5249+
format_expr_type(), format_type(Instance(info, t.args), self.options)
52315250
),
52325251
expr,
52335252
)
@@ -6012,7 +6031,9 @@ def check_subtype(
60126031
note_msg = ""
60136032
notes = notes or []
60146033
if subtype_label is not None or supertype_label is not None:
6015-
subtype_str, supertype_str = format_type_distinctly(orig_subtype, orig_supertype)
6034+
subtype_str, supertype_str = format_type_distinctly(
6035+
orig_subtype, orig_supertype, options=self.options
6036+
)
60166037
if subtype_label is not None:
60176038
extra_info.append(subtype_label + " " + subtype_str)
60186039
if supertype_label is not None:

mypy/checkexpr.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3967,7 +3967,12 @@ def visit_type_application(self, tapp: TypeApplication) -> Type:
39673967
if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias):
39683968
# Subscription of a (generic) alias in runtime context, expand the alias.
39693969
item = expand_type_alias(
3970-
tapp.expr.node, tapp.types, self.chk.fail, tapp.expr.node.no_args, tapp
3970+
tapp.expr.node,
3971+
tapp.types,
3972+
self.chk.fail,
3973+
tapp.expr.node.no_args,
3974+
tapp,
3975+
self.chk.options,
39713976
)
39723977
item = get_proper_type(item)
39733978
if isinstance(item, Instance):
@@ -4032,7 +4037,12 @@ class LongName(Generic[T]): ...
40324037
disallow_any = self.chk.options.disallow_any_generics and self.is_callee
40334038
item = get_proper_type(
40344039
set_any_tvars(
4035-
alias, ctx.line, ctx.column, disallow_any=disallow_any, fail=self.msg.fail
4040+
alias,
4041+
ctx.line,
4042+
ctx.column,
4043+
self.chk.options,
4044+
disallow_any=disallow_any,
4045+
fail=self.msg.fail,
40364046
)
40374047
)
40384048
if isinstance(item, Instance):

mypy/checkmember.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,10 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
637637
dunder_get = descriptor_type.type.get_method("__get__")
638638
if dunder_get is None:
639639
mx.msg.fail(
640-
message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(descriptor_type), mx.context
640+
message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(
641+
descriptor_type.str_with_options(mx.msg.options)
642+
),
643+
mx.context,
641644
)
642645
return AnyType(TypeOfAny.from_error)
643646

@@ -694,7 +697,10 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
694697

695698
if not isinstance(inferred_dunder_get_type, CallableType):
696699
mx.msg.fail(
697-
message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(descriptor_type), mx.context
700+
message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(
701+
descriptor_type.str_with_options(mx.msg.options)
702+
),
703+
mx.context,
698704
)
699705
return AnyType(TypeOfAny.from_error)
700706

mypy/checkpattern.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from mypy.meet import narrow_declared_type
1717
from mypy.messages import MessageBuilder
1818
from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, TypeInfo, Var
19+
from mypy.options import Options
1920
from mypy.patterns import (
2021
AsPattern,
2122
ClassPattern,
@@ -104,7 +105,11 @@ class PatternChecker(PatternVisitor[PatternType]):
104105
# non_sequence_match_type_names
105106
non_sequence_match_types: list[Type]
106107

107-
def __init__(self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin) -> None:
108+
options: Options
109+
110+
def __init__(
111+
self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin, options: Options
112+
) -> None:
108113
self.chk = chk
109114
self.msg = msg
110115
self.plugin = plugin
@@ -114,6 +119,7 @@ def __init__(self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: P
114119
self.non_sequence_match_types = self.generate_types_from_names(
115120
non_sequence_match_type_names
116121
)
122+
self.options = options
117123

118124
def accept(self, o: Pattern, type_context: Type) -> PatternType:
119125
self.type_context.append(type_context)
@@ -458,8 +464,8 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
458464
elif isinstance(type_info, TypeAlias):
459465
typ = type_info.target
460466
else:
461-
if isinstance(type_info, Var):
462-
name = str(type_info.type)
467+
if isinstance(type_info, Var) and type_info.type is not None:
468+
name = type_info.type.str_with_options(self.options)
463469
else:
464470
name = type_info.name
465471
self.msg.fail(message_registry.CLASS_PATTERN_TYPE_REQUIRED.format(name), o.class_ref)
@@ -508,7 +514,12 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
508514
)
509515
has_local_errors = local_errors.has_new_errors()
510516
if has_local_errors:
511-
self.msg.fail(message_registry.MISSING_MATCH_ARGS.format(typ), o)
517+
self.msg.fail(
518+
message_registry.MISSING_MATCH_ARGS.format(
519+
typ.str_with_options(self.options)
520+
),
521+
o,
522+
)
512523
return self.early_non_match()
513524

514525
proper_match_args_type = get_proper_type(match_args_type)
@@ -573,7 +584,10 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
573584
if has_local_errors or key_type is None:
574585
key_type = AnyType(TypeOfAny.from_error)
575586
self.msg.fail(
576-
message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format(typ, keyword), pattern
587+
message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format(
588+
typ.str_with_options(self.options), keyword
589+
),
590+
pattern,
577591
)
578592

579593
inner_type, inner_rest_type, inner_captures = self.accept(pattern, key_type)

mypy/checkstrformat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ def apply_field_accessors(
588588
return repl
589589
assert spec.field
590590

591-
temp_errors = Errors()
591+
temp_errors = Errors(self.chk.options)
592592
dummy = DUMMY_FIELD_NAME + spec.field[len(spec.key) :]
593593
temp_ast: Node = parse(
594594
dummy, fnam="<format>", module=None, options=self.chk.options, errors=temp_errors

0 commit comments

Comments
 (0)