From 98d59a280628bccc4320f12e71aea7e083be5e22 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 30 May 2025 12:49:57 +0200 Subject: [PATCH 1/5] Deprecated --force-uppercase-builtins flag --- CHANGELOG.md | 4 +++ mypy/main.py | 4 +++ mypy/messages.py | 28 ++++++------------ mypy/options.py | 7 +++++ mypy/types.py | 11 ++------ test-data/unit/check-lowercase.test | 44 +++++------------------------ 6 files changed, 33 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d58ce6a1b3d..a385930422048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Next Release +### Deprecated Flag: \--force-uppercase-builtins + +Mypy only supports Python 3.9+. The \--force-uppercase-builtins flag is now deprecated and a no-op. It will be removed in a future version. + ## Mypy 1.16 We’ve just uploaded mypy 1.16 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). diff --git a/mypy/main.py b/mypy/main.py index 6ebf32ded6e13..cf4948e06c050 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -801,6 +801,7 @@ def add_invertible_flag( help="Disable strict Optional checks (inverse: --strict-optional)", ) + # This flag is deprecated, Mypy only supports Python 3.9+ add_invertible_flag( "--force-uppercase-builtins", default=False, help=argparse.SUPPRESS, group=none_group ) @@ -1494,6 +1495,9 @@ def set_strict_flags() -> None: if options.strict_concatenate and not strict_option_set: print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") + if options.force_uppercase_builtins: + print("Warning: --force-uppercase-builtins is deprecated; Mypy only supports Python 3.9+.") + # Set target. if special_opts.modules + special_opts.packages: options.build_type = BuildType.MODULE diff --git a/mypy/messages.py b/mypy/messages.py index 2e07d7f634983..567f29261f8d6 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1823,13 +1823,10 @@ def need_annotation_for_var( recommended_type = f"Optional[{type_dec}]" elif node.type.type.fullname in reverse_builtin_aliases: # partial types other than partial None - alias = reverse_builtin_aliases[node.type.type.fullname] - alias = alias.split(".")[-1] - if alias == "Dict": + name = node.type.type.fullname.partition(".")[2] + if name == "dict": type_dec = f"{type_dec}, {type_dec}" - if self.options.use_lowercase_names(): - alias = alias.lower() - recommended_type = f"{alias}[{type_dec}]" + recommended_type = f"{name}[{type_dec}]" if recommended_type is not None: hint = f' (hint: "{node.name}: {recommended_type} = ...")' @@ -2419,8 +2416,7 @@ 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 > MAX_TUPLE_ITEMS: - return "{}[{}, {}, ... <{} more items>]".format( - "tuple" if self.options.use_lowercase_names() else "Tuple", + return "tuple[{}, {}, ... <{} more items>]".format( format_type_bare(typ.items[0], self.options), format_type_bare(typ.items[1], self.options), str(item_cnt - 2), @@ -2595,10 +2591,7 @@ 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 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): + if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): base_str = itype.type.fullname else: base_str = itype.type.name @@ -2609,7 +2602,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' if options.use_lowercase_names() else 'Tuple'}[{item_type_str}, ...]" + return f"tuple[{item_type_str}, ...]" else: # There are type arguments. Convert the arguments to strings. return f"{base_str}[{format_list(itype.args)}]" @@ -2645,11 +2638,7 @@ def format_literal_value(typ: LiteralType) -> str: if typ.partial_fallback.type.fullname != "builtins.tuple": return format(typ.partial_fallback) type_items = format_list(typ.items) or "()" - if options.use_lowercase_names(): - s = f"tuple[{type_items}]" - else: - s = f"Tuple[{type_items}]" - return s + return f"tuple[{type_items}]" elif isinstance(typ, TypedDictType): # If the TypedDictType is named, return the name if not typ.is_anonymous(): @@ -2721,8 +2710,7 @@ def format_literal_value(typ: LiteralType) -> str: elif isinstance(typ, UninhabitedType): return "Never" elif isinstance(typ, TypeType): - type_name = "type" if options.use_lowercase_names() else "Type" - return f"{type_name}[{format(typ.item)}]" + return f"type[{format(typ.item)}]" elif isinstance(typ, FunctionLike): func = typ if func.is_type_obj(): diff --git a/mypy/options.py b/mypy/options.py index 52afd27211ed0..1a99e893de67c 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -4,6 +4,7 @@ import re import sys import sysconfig +import warnings from collections.abc import Mapping from re import Pattern from typing import Any, Callable, Final @@ -400,6 +401,7 @@ def __init__(self) -> None: self.disable_bytearray_promotion = False self.disable_memoryview_promotion = False + # Deprecated, Mypy only supports Python 3.9+ self.force_uppercase_builtins = False self.force_union_syntax = False @@ -413,6 +415,11 @@ def __init__(self) -> None: self.mypyc_skip_c_generation = False def use_lowercase_names(self) -> bool: + warnings.warn( + "options.use_lowercase_names is deprecated and will be removed in a future version", + DeprecationWarning, + stacklevel=2, + ) if self.python_version >= (3, 9): return not self.force_uppercase_builtins return False diff --git a/mypy/types.py b/mypy/types.py index 41a958ae93cca..c37e44b183bf0 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3455,12 +3455,11 @@ def visit_overloaded(self, t: Overloaded, /) -> str: def visit_tuple_type(self, t: TupleType, /) -> str: s = self.list_str(t.items) or "()" - 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_name}[{s}, fallback={t.partial_fallback.accept(self)}]" - return f"{tuple_name}[{s}]" + return f"tuple[{s}, fallback={t.partial_fallback.accept(self)}]" + return f"tuple[{s}]" def visit_typeddict_type(self, t: TypedDictType, /) -> str: def item_str(name: str, typ: str) -> str: @@ -3502,11 +3501,7 @@ def visit_ellipsis_type(self, t: EllipsisType, /) -> str: return "..." def visit_type_type(self, t: TypeType, /) -> str: - if self.options.use_lowercase_names(): - type_name = "type" - else: - type_name = "Type" - return f"{type_name}[{t.item.accept(self)}]" + return f"type[{t.item.accept(self)}]" def visit_placeholder_type(self, t: PlaceholderType, /) -> str: return f"" diff --git a/test-data/unit/check-lowercase.test b/test-data/unit/check-lowercase.test index 51a833614a336..d19500327255f 100644 --- a/test-data/unit/check-lowercase.test +++ b/test-data/unit/check-lowercase.test @@ -1,64 +1,34 @@ - -[case testTupleLowercaseSettingOff] -# flags: --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: --no-force-uppercase-builtins +[case testTupleLowercase] 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: --force-uppercase-builtins -x = [3] -x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "List[int]") - -[case testListLowercaseSettingOn] -# flags: --no-force-uppercase-builtins +[case testListLowercase] x = [3] x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "list[int]") -[case testDictLowercaseSettingOff] -# flags: --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: --no-force-uppercase-builtins +[case testDictLowercase] x = {"key": "value"} x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "dict[str, str]") -[case testSetLowercaseSettingOff] -# flags: --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: --no-force-uppercase-builtins +[case testSetLowercase] x = {3} x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "set[int]") [builtins fixtures/set.pyi] -[case testTypeLowercaseSettingOff] -# flags: --no-force-uppercase-builtins +[case testTypeLowercase] x: type[type] y: int y = x # E: Incompatible types in assignment (expression has type "type[type]", variable has type "int") -[case testLowercaseSettingOnTypeAnnotationHint] -# flags: --no-force-uppercase-builtins +[case testLowercaseTypeAnnotationHint] x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") y = {} # E: Need type annotation for "y" (hint: "y: dict[, ] = ...") z = set() # E: Need type annotation for "z" (hint: "z: set[] = ...") [builtins fixtures/primitives.pyi] -[case testLowercaseSettingOnRevealTypeType] -# flags: --no-force-uppercase-builtins +[case testLowercaseRevealTypeType] def f(t: type[int]) -> None: reveal_type(t) # N: Revealed type is "type[builtins.int]" reveal_type(f) # N: Revealed type is "def (t: type[builtins.int])" From f97c2d836a244d579d2c969bbc6336967fe6b5ec Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 30 May 2025 12:49:57 +0200 Subject: [PATCH 2/5] Update docs --- docs/source/command_line.rst | 5 ----- docs/source/config_file.rst | 8 -------- 2 files changed, 13 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index dfed280d12ed2..aa19420558ecd 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -936,11 +936,6 @@ in error messages. useful or they may be overly noisy. If ``N`` is negative, there is no limit. The default limit is -1. -.. option:: --force-uppercase-builtins - - Always use ``List`` instead of ``list`` in error messages, - even on Python 3.9+. - .. option:: --force-union-syntax Always use ``Union[]`` and ``Optional[]`` for union types diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 9f23617b94811..b4f134f26cb14 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -922,14 +922,6 @@ These options may only be set in the global section (``[mypy]``). Show absolute paths to files. -.. confval:: force_uppercase_builtins - - :type: boolean - :default: False - - Always use ``List`` instead of ``list`` in error messages, - even on Python 3.9+. - .. confval:: force_union_syntax :type: boolean From c6a1d2cf7a2afb1ca559d71652c38f9c48c02289 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 30 May 2025 21:54:58 +0200 Subject: [PATCH 3/5] Always return true for use_lowercase_names --- mypy/options.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mypy/options.py b/mypy/options.py index 1a99e893de67c..4a89ef529c071 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -416,13 +416,11 @@ def __init__(self) -> None: def use_lowercase_names(self) -> bool: warnings.warn( - "options.use_lowercase_names is deprecated and will be removed in a future version", + "options.use_lowercase_names() is deprecated and will be removed in a future version", DeprecationWarning, stacklevel=2, ) - if self.python_version >= (3, 9): - return not self.force_uppercase_builtins - return False + return True def use_or_syntax(self) -> bool: if self.python_version >= (3, 10): From 062b96c66cdda7a991d283b57f00ccadf9446ea2 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 2 Jun 2025 01:02:20 +0200 Subject: [PATCH 4/5] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a385930422048..b09916919d8a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Mypy only supports Python 3.9+. The \--force-uppercase-builtins flag is now deprecated and a no-op. It will be removed in a future version. +Contributed by Marc Mueller (PR [19176](https://github.com/python/mypy/pull/19176)) + ## Mypy 1.16 We’ve just uploaded mypy 1.16 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). From cec75cdf7c5139f967b7e511597baa85e6a81c46 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:37:44 +0200 Subject: [PATCH 5/5] Spelling --- mypy/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/main.py b/mypy/main.py index cf4948e06c050..16e9e035bf2ef 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1496,7 +1496,7 @@ def set_strict_flags() -> None: print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") if options.force_uppercase_builtins: - print("Warning: --force-uppercase-builtins is deprecated; Mypy only supports Python 3.9+.") + print("Warning: --force-uppercase-builtins is deprecated; mypy only supports Python 3.9+") # Set target. if special_opts.modules + special_opts.packages: