Skip to content

Commit 28f00e5

Browse files
Merge branch 'master' into weakref-call
2 parents e50b118 + b025bda commit 28f00e5

Some content is hidden

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

55 files changed

+1116
-207
lines changed

.github/workflows/mypy_primer.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ jobs:
6767
--debug \
6868
--additional-flags="--debug-serialize" \
6969
--output concise \
70-
--show-speed-regression \
7170
| tee diff_${{ matrix.shard-index }}.txt
7271
) || [ $? -eq 1 ]
7372
- if: ${{ matrix.shard-index == 0 }}

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
## Next Release
44

5+
### Remove Support for targeting Python 3.8
6+
7+
Mypy now requires `--python-version 3.9` or greater. Support for only Python 3.8 is
8+
fully removed now. Given an unsupported version, mypy will default to the oldest
9+
supported one, currently 3.9.
10+
11+
This change is necessary because typeshed stopped supporting Python 3.8 after it
12+
reached its End of Life in October 2024.
13+
14+
Contributed by Marc Mueller
15+
(PR [19157](https://github.com/python/mypy/pull/19157), PR [19162](https://github.com/python/mypy/pull/19162)).
16+
17+
### Initial Support for Python 3.14
18+
19+
Mypy is now tested on 3.14 and mypyc works with 3.14.0b3 and later.
20+
Mypyc compiled wheels of mypy itself will be available for new versions after 3.14.0rc1 is released.
21+
22+
Note that not all new features might be supported just yet.
23+
24+
Contributed by Marc Mueller (PR [19164](https://github.com/python/mypy/pull/19164))
25+
26+
### Deprecated Flag: \--force-uppercase-builtins
27+
28+
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.
29+
30+
Contributed by Marc Mueller (PR [19176](https://github.com/python/mypy/pull/19176))
31+
532
## Mypy 1.16
633

734
We’ve just uploaded mypy 1.16 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).

docs/source/command_line.rst

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ of the above sections.
845845
x = 'a string'
846846
x.trim() # error: "str" has no attribute "trim" [attr-defined]
847847
848+
848849
.. _configuring-error-messages:
849850

850851
Configuring error messages
@@ -936,11 +937,6 @@ in error messages.
936937
useful or they may be overly noisy. If ``N`` is negative, there is
937938
no limit. The default limit is -1.
938939

939-
.. option:: --force-uppercase-builtins
940-
941-
Always use ``List`` instead of ``list`` in error messages,
942-
even on Python 3.9+.
943-
944940
.. option:: --force-union-syntax
945941

946942
Always use ``Union[]`` and ``Optional[]`` for union types

docs/source/config_file.rst

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -922,14 +922,6 @@ These options may only be set in the global section (``[mypy]``).
922922

923923
Show absolute paths to files.
924924

925-
.. confval:: force_uppercase_builtins
926-
927-
:type: boolean
928-
:default: False
929-
930-
Always use ``List`` instead of ``list`` in error messages,
931-
even on Python 3.9+.
932-
933925
.. confval:: force_union_syntax
934926

935927
:type: boolean

docs/source/error_code_list2.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,3 +612,44 @@ Example:
612612
# mypy: disallow-any-explicit
613613
from typing import Any
614614
x: Any = 1 # Error: Explicit "Any" type annotation [explicit-any]
615+
616+
617+
.. _code-exhaustive-match:
618+
619+
Check that match statements match exhaustively [match-exhaustive]
620+
-----------------------------------------------------------------------
621+
622+
If enabled with :option:`--enable-error-code exhaustive-match <mypy --enable-error-code>`,
623+
mypy generates an error if a match statement does not match all possible cases/types.
624+
625+
626+
Example:
627+
628+
.. code-block:: python
629+
630+
import enum
631+
632+
633+
class Color(enum.Enum):
634+
RED = 1
635+
BLUE = 2
636+
637+
val: Color = Color.RED
638+
639+
# OK without --enable-error-code exhaustive-match
640+
match val:
641+
case Color.RED:
642+
print("red")
643+
644+
# With --enable-error-code exhaustive-match
645+
# Error: Match statement has unhandled case for values of type "Literal[Color.BLUE]"
646+
match val:
647+
case Color.RED:
648+
print("red")
649+
650+
# OK with or without --enable-error-code exhaustive-match, since all cases are handled
651+
match val:
652+
case Color.RED:
653+
print("red")
654+
case _:
655+
print("other")

docs/source/literal_types.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ If we forget to handle one of the cases, mypy will generate an error:
468468
assert_never(direction) # E: Argument 1 to "assert_never" has incompatible type "Direction"; expected "NoReturn"
469469
470470
Exhaustiveness checking is also supported for match statements (Python 3.10 and later).
471+
For match statements specifically, inexhaustive matches can be caught
472+
without needing to use ``assert_never`` by using
473+
:option:`--enable-error-code exhaustive-match <mypy --enable-error-code>`.
474+
471475

472476
Extra Enum checks
473477
*****************

mypy/checker.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,7 +2449,7 @@ def erase_override(t: Type) -> Type:
24492449
if not is_subtype(original_arg_type, erase_override(override_arg_type)):
24502450
context: Context = node
24512451
if isinstance(node, FuncDef) and not node.is_property:
2452-
arg_node = node.arguments[i + len(override.bound_args)]
2452+
arg_node = node.arguments[i + override.bound()]
24532453
if arg_node.line != -1:
24542454
context = arg_node
24552455
self.msg.argument_incompatible_with_supertype(
@@ -2664,7 +2664,7 @@ def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None:
26642664
continue
26652665
if not is_subtype(tv.default, tv.upper_bound):
26662666
self.fail("TypeVar default must be a subtype of the bound type", tv)
2667-
if tv.values and not any(tv.default == value for value in tv.values):
2667+
if tv.values and not any(is_same_type(tv.default, value) for value in tv.values):
26682668
self.fail("TypeVar default must be one of the constraint types", tv)
26692669

26702670
def check_enum(self, defn: ClassDef) -> None:
@@ -5455,6 +5455,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
54555455
inferred_types = self.infer_variable_types_from_type_maps(type_maps)
54565456

54575457
# The second pass narrows down the types and type checks bodies.
5458+
unmatched_types: TypeMap = None
54585459
for p, g, b in zip(s.patterns, s.guards, s.bodies):
54595460
current_subject_type = self.expr_checker.narrow_type_from_binder(
54605461
named_subject, subject_type
@@ -5511,6 +5512,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
55115512
else:
55125513
self.accept(b)
55135514
self.push_type_map(else_map, from_assignment=False)
5515+
unmatched_types = else_map
5516+
5517+
if unmatched_types is not None:
5518+
for typ in list(unmatched_types.values()):
5519+
self.msg.match_statement_inexhaustive_match(typ, s)
55145520

55155521
# This is needed due to a quirk in frame_context. Without it types will stay narrowed
55165522
# after the match.
@@ -7691,9 +7697,13 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None:
76917697
types: list[TypeRange] = []
76927698
for typ in all_types:
76937699
if isinstance(typ, FunctionLike) and typ.is_type_obj():
7694-
# Type variables may be present -- erase them, which is the best
7695-
# we can do (outside disallowing them here).
7696-
erased_type = erase_typevars(typ.items[0].ret_type)
7700+
# If a type is generic, `isinstance` can only narrow its variables to Any.
7701+
any_parameterized = fill_typevars_with_any(typ.type_object())
7702+
# Tuples may have unattended type variables among their items
7703+
if isinstance(any_parameterized, TupleType):
7704+
erased_type = erase_typevars(any_parameterized)
7705+
else:
7706+
erased_type = any_parameterized
76977707
types.append(TypeRange(erased_type, is_upper_bound=False))
76987708
elif isinstance(typ, TypeType):
76997709
# Type[A] means "any type that is a subtype of A" rather than "precisely type A"

mypy/checkexpr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4975,7 +4975,7 @@ def apply_type_arguments_to_callable(
49754975
tp.fallback,
49764976
name="tuple",
49774977
definition=tp.definition,
4978-
bound_args=tp.bound_args,
4978+
is_bound=tp.is_bound,
49794979
)
49804980
self.msg.incompatible_type_application(
49814981
min_arg_count, len(type_vars), len(args), ctx
@@ -6171,7 +6171,7 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> Type:
61716171
):
61726172
if not is_subtype(p_default, e.upper_bound):
61736173
self.chk.fail("TypeVar default must be a subtype of the bound type", e)
6174-
if e.values and not any(p_default == value for value in e.values):
6174+
if e.values and not any(is_same_type(p_default, value) for value in e.values):
61756175
self.chk.fail("TypeVar default must be one of the constraint types", e)
61766176
return AnyType(TypeOfAny.special_form)
61776177

mypy/checkmember.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ def analyze_var(
921921
bound_items = []
922922
for ct in call_type.items if isinstance(call_type, UnionType) else [call_type]:
923923
p_ct = get_proper_type(ct)
924-
if isinstance(p_ct, FunctionLike) and not p_ct.is_type_obj():
924+
if isinstance(p_ct, FunctionLike) and (not p_ct.bound() or var.is_property):
925925
item = expand_and_bind_callable(p_ct, var, itype, name, mx, is_trivial_self)
926926
else:
927927
item = expand_without_binding(ct, var, itype, original_itype, mx)
@@ -1484,19 +1484,20 @@ def bind_self_fast(method: F, original_type: Type | None = None) -> F:
14841484
items = [bind_self_fast(c, original_type) for c in method.items]
14851485
return cast(F, Overloaded(items))
14861486
assert isinstance(method, CallableType)
1487-
if not method.arg_types:
1487+
func: CallableType = method
1488+
if not func.arg_types:
14881489
# Invalid method, return something.
1489-
return cast(F, method)
1490-
if method.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
1490+
return method
1491+
if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
14911492
# See typeops.py for details.
1492-
return cast(F, method)
1493+
return method
14931494
original_type = get_proper_type(original_type)
14941495
if isinstance(original_type, CallableType) and original_type.is_type_obj():
14951496
original_type = TypeType.make_normalized(original_type.ret_type)
1496-
res = method.copy_modified(
1497-
arg_types=method.arg_types[1:],
1498-
arg_kinds=method.arg_kinds[1:],
1499-
arg_names=method.arg_names[1:],
1500-
bound_args=[original_type],
1497+
res = func.copy_modified(
1498+
arg_types=func.arg_types[1:],
1499+
arg_kinds=func.arg_kinds[1:],
1500+
arg_names=func.arg_names[1:],
1501+
is_bound=True,
15011502
)
15021503
return cast(F, res)

mypy/config_parser.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@
2828
_INI_PARSER_CALLABLE: _TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES]
2929

3030

31+
class VersionTypeError(argparse.ArgumentTypeError):
32+
"""Provide a fallback value if the Python version is unsupported."""
33+
34+
def __init__(self, *args: Any, fallback: tuple[int, int]) -> None:
35+
self.fallback = fallback
36+
super().__init__(*args)
37+
38+
3139
def parse_version(v: str | float) -> tuple[int, int]:
3240
m = re.match(r"\A(\d)\.(\d+)\Z", str(v))
3341
if not m:
@@ -44,7 +52,7 @@ def parse_version(v: str | float) -> tuple[int, int]:
4452
if isinstance(v, float):
4553
msg += ". You may need to put quotes around your Python version"
4654

47-
raise argparse.ArgumentTypeError(msg)
55+
raise VersionTypeError(msg, fallback=defaults.PYTHON3_VERSION_MIN)
4856
else:
4957
raise argparse.ArgumentTypeError(
5058
f"Python major version '{major}' out of range (must be 3)"
@@ -548,6 +556,9 @@ def parse_section(
548556
continue
549557
try:
550558
v = ct(section.get(key))
559+
except VersionTypeError as err_version:
560+
print(f"{prefix}{key}: {err_version}", file=stderr)
561+
v = err_version.fallback
551562
except argparse.ArgumentTypeError as err:
552563
print(f"{prefix}{key}: {err}", file=stderr)
553564
continue

0 commit comments

Comments
 (0)